以REST风格调用资源上的服务器端方法

请记住,我对REST有一个基本的了解。 比方说,我有这个url:

http://api.animals.com/v1/dogs/1/ 

而现在,我想让服务器让狗叫。 只有服务器知道如何做到这一点。 比方说,我想让它在CRON的工作上运行,让这只狗每隔10分钟就要吠叫一次。 这个电话是什么样的? 我有点想这样做:

url请求:

 ACTION http://api.animals.com/v1/dogs/1/ 

在请求体中:

 {"action":"bark"} 

在你为我自己制作HTTP方法而生气之前,请帮助我,让我更好地了解如何以REST方式调用服务器端方法。 🙂

编辑澄清

有关“树皮”方法的更多说明。 以下是一些可能导致不同结构化API调用的选项:

  1. 树皮只是发送一封电子邮件到dog.email并没有logging。
  2. 树皮发送电子邮件到dog.email和增量dog.barkCount由1。
  3. 树皮创造一个新的“树皮”logging与树皮时间戳记树皮发生时。 它也使dog.barkCount增加1。
  4. 树皮运行系统命令从Github下拉最新版本的狗代码。 然后它发送一条短信给dog.owner,告诉他们新的狗码正在生产中。

为什么瞄准一个RESTfuldevise?

RESTful原则带来了使Web站点容易的function (对于随机的人类用户来说 “冲浪”) 到Web服务APIdevise ,因此它们对于程序员来说很容易使用。 REST不好,因为它是REST,这很好,因为它是好的。 主要是因为它很简单

简单的HTTP(没有SOAP信封和单个URI重载的POST服务)的简单性, 有些人可能称之为“function缺乏” ,实际上是它最大的优点 。 HTTP请求你具有可寻址性无状态性 :两个基本的devise决定,保持HTTP可扩展到当今的大型站点(和大型服务)。

但是REST并不是silverlight的牛鼻子: 有时RPC风格 (“远程过程调用” – 比如SOAP) 可能是合适的 ,有时其他需要优先于Web的优点。 这可以。 我们不太喜欢的是不必要的复杂性 。 程序员或公司往往会引入RPC风格的服务来处理一个简单的旧HTTP可以处理的工作。 其结果是,HTTP被简化为传输协议,用于解释真正正在进行的巨大XML有效载荷(而不是URI或HTTP方法给出的线索)。 由此产生的服务太复杂,无法进行debugging,除非客户按照开发人员的意图进行准确的设置 ,否则无法运行。

Java / C#代码也可以不是面向对象的,只是使用HTTP并不能使devise成为RESTful。 人们可能会被卷入思考他的服务的行动和应该被调用的远程方法 。 难怪这将主要在RPC风格的服务(或REST-RPC混合)中结束。 第一步是思考不同。 REST风格的devise可以通过多种方式来实现,一种方式(最简单的,也许可以这样说)是从资源而不是行为的angular度考虑应用程序:

  • 而不是根据行动来思考(“在地图上search地点”),
  • 根据该行动的结果(“符合search标准的地图列表”)来考虑。

我会在下面的例子。 (REST的另一个关键方面是使用HATEOAS–我不会在这里刷,但是我可以在另一个post中快速地谈论它)。

关于第一个devise

我们来看看这个提议的devise:

 ACTION http://api.animals.com/v1/dogs/1/ 

首先,我们不应该考虑创build一个新的HTTP动词ACTION )。 一般来说,这是不受欢迎的 ,原因如下:

  • (1)只给出服务URI,“随机”程序员将如何知道ACTION谓词的存在?
  • (2)如果程序员知道它存在,他将如何知道它的语义? 那个动词是什么意思?
  • (3)我们应该期望动词具有哪些属性(安全性,幂等性)?
  • (4)如果程序员有一个非常简单的客户端,只处理标准的HTTP动词呢?
  • (5)

现在让我们考虑使用POST (我将讨论为什么在下面,现在就拿我的话说):

 POST /v1/dogs/1/ HTTP/1.1 Host: api.animals.com {"action":"bark"} 

可能是好的,但只有在以下情况下

  • {"action":"bark"}是一个文件; 和
  • /v1/dogs/1/是一个“文件处理器”(factory-like)的URI。 “文档处理器”是一个你只是“扔东西”和“忘记”的URI,处理器可能会在“投掷”之后将你redirect到一个新创build的资源。 例如,用于在消息代理服务器上发布消息的URI,在发布之后,它将把您redirect到显示消息处理状态的URI。

我对你的系统了解不多,但我已经打赌两个都不是真的:

  • {"action":"bark"} 不是一个文件 ,它实际上你试图忍者潜入服务的方法; 和
  • /v1/dogs/1/ URI表示一个“狗”资源(可能是id==1的狗),而不是文档处理器。

所以我们现在知道的是,上面的devise不是那么RESTful,但究竟是什么? 这有什么不好? 基本上,这是错误的,因为这是复杂的URI,具有复杂的意义。 你无法从中推断出任何东西。 一个程序员如何知道一只狗有一个可以秘密注入POSTbark动作?

devise你的问题的API调用

那么让我们来追逐一下,试着从资源的angular度来思考这些吠叫。 请允许我引用Restful Web Services书籍:

POST请求是尝试从现有资源创build新资源。 现有资源可能是数据结构意义上的新资源的父代,树的根是所有叶节点的父代。 或者现有资源可能是一个特殊的“工厂”资源,其唯一目的是生成其他资源。 与POST请求一起发送的表示描述了新资源的初始状态。 和PUT一样, POST请求根本不需要包含表示。

根据上面的描述,我们可以看到, bark可以被build模为一个dog的子资源 (因为bark被包含在一个狗内,即一个狗叫“树皮”)。

从这个推理我们已经得到:

  • 方法是POST
  • 资源是/barks barks,狗的子资源: /v1/dogs/1/barks ,代表一个bark “工厂”。 该URI对于每只狗都是唯一的(因为它位于/v1/dogs/{id}之下)。

现在,您的列表中的每个案例都有特定的行为。

树皮只是发一封电子邮件给dog.email并且什么也没有logging。

首先是吠叫(发送电子邮件)同步或asynchronous任务? 其次, bark请求需要任何文件(电子邮件,也许)或是空的?

1.1树皮发送一封邮件到dog.email并且不logging(作为同步任务)

这种情况很简单。 对barks工厂资源的调用立即产生一个树皮(发送一封电子邮件),并立即给出响应(如果确定或不正确):

 POST /v1/dogs/1/barks HTTP/1.1 Host: api.animals.com Authorization: Basic mAUhhuE08u724bh249a2xaP= (entity-body is empty - or, if you require a **document**, place it here) 200 OK 

当它没有logging(变化)时, 200 OK就足够了。 这表明一切都如预期一样。

1.2树皮发送一封邮件到dog.email并且不logging(作为一个asynchronous任务)

在这种情况下,客户端必须有一种方法来跟踪bark任务。 bark任务应该是一个拥有自己URI的资源。

 POST /v1/dogs/1/barks HTTP/1.1 Host: api.animals.com Authorization: Basic mAUhhuE08u724bh249a2xaP= (document body, if needed) 202 Accepted Location: http://api.animals.com/v1/dogs/1/bark/a65h44 

这样,每个bark都是可追溯的。 客户端然后可以发出一个GETbark URI来知道它的当前状态。 甚至可能使用DELETE来取消它。

2.树皮发送一封电子邮件到dog.email和增加dog.barkCount 1

如果你想让客户知道dog资源被改变,这个可能会更棘手。

 POST /v1/dogs/1/barks HTTP/1.1 Host: api.animals.com Authorization: Basic mAUhhuE08u724bh249a2xaP= (document body, if needed) 303 See Other Location: http://api.animals.com/v1/dogs/1 

在这种情况下, location标题的意图是让客户知道他应该看看dog 。 从HTTP RFC约303

此方法主要用于允许POST激活的脚本的输出将用户代理redirect到选定的资源。

如果任务是asynchronous的,就像1.2情况一样需要一个bark子资源,并且当任务完成时303应该以GET .../bark/Y返回。

树皮创造一个新的“树皮”logging与树皮时间戳记树皮发生时。 它也使dog.barkCount增加1。

 POST /v1/dogs/1/barks HTTP/1.1 Host: api.animals.com Authorization: Basic mAUhhuE08u724bh249a2xaP= (document body, if needed) 201 Created Location: http://api.animals.com/v1/dogs/1/bark/a65h44 

在这里, bark是由于请求而创build的,所以应用了“ 201 Created状态201 Created

如果创build是asynchronous的,则需要202 Accepted ( 正如HTTP RFC所述 )。

保存的时间戳是bark资源的一部分,可以通过GET进行检索。 更新后的狗可以在GET dogs/X/bark/Y “logging”。

4.树皮运行一个系统命令,从Github下载最新版本的狗代码。 然后它发送一条短信给dog.owner,告诉他们新的狗码正在生产中。

这个的措辞是复杂的,但它几乎是一个简单的asynchronous任务:

 POST /v1/dogs/1/barks HTTP/1.1 Host: api.animals.com Authorization: Basic mAUhhuE08u724bh249a2xaP= (document body, if needed) 202 Accepted Location: http://api.animals.com/v1/dogs/1/bark/a65h44 

然后客户会发送GET/v1/dogs/1/bark/a65h44以了解当前状态(如果代码被拉出,电子邮件被发送给所有者等)。 只要狗改变, 303就可以了。

包起来

引用Roy Fielding :

REST对于方法的唯一要求是对所有资源统一定义它们(即为了理解请求的含义,中间人不必知道资源types)。

在上面的例子中, POST是统一devise的。 它会使狗“ bark ”。 这是不安全的(意思是树皮对资源有影响),也不是幂等的(每个请求产生一个新的bark ),这很适合POST动词。

一个程序员会知道:一个POST发出barksbark 。 响应状态代码(在必要时也与实体主体和头部一起)做解释什么改变以及客户如何进行的工作。

注意:使用的主要来源是“ Restful Web Services ”一书, HTTP RF C和Roy Fielding的博客 。


更新:

最初的问题是关于URI的devise问题

 ACTION http://api.animals.com/v1/dogs/1/?action=bark 

下面是为什么它不是一个好的select的解释:

客户端如何告诉服务器如何处理数据是方法信息

  • REST风格的Web服务在HTTP方法中传递方法信息。
  • 典型的RPC-Style和SOAP服务将它们保留在实体主体和HTTP头中。

数据[客户端要服务器]操作的哪一部分范围信息

  • RESTful服务使用URI。 SOAP / RPC-Style服务再次使用entity-body和HTTP头。

例如,请使用Google的http://www.google.com/search?q=DOG URI。 在那里,方法信息是GET ,范围信息是/search?q=DOG

长话短说:

  • RESTful体系结构中 ,方法信息进入HTTP方法。
  • 面向资源的体系结构中 ,范围信息进入URI。

经验法则:

如果HTTP方法与方法信息不匹配,则该服务不是RESTful。 如果范围信息不在URI中,则服务不是面向资源的。

你可以把“bark” “action”放在URL(或者entity-body)中,然后使用POST 。 没有问题,它的工作原理,可能是最简单的方法, 但这不是RESTful

为了让你的服​​务真正RESTful,你可能需要退后一步,想想你真正想在这里做什么(它会对资源有什么影响)。

我不能谈论您的具体业务需求,但让我举个例子:考虑一个RESTful订购服务,订单在URI如example.com/order/123

现在说我们要取消订单,我们该怎么做? 有人可能会想,这是一个“取消” “行动”,并将其devise为POST example.com/order/123?do=cancel

正如我们上面所谈到的那样,这不是RESTful。 相反,我们可以用一个canceled元素发送到true来把一个新的order表示:

 PUT /order/123 HTTP/1.1 Content-Type: application/xml <order id="123"> <customer id="89987">...</customer> <canceled>true</canceled> ... </order> 

就是这样。 如果订单无法取消,则可以返回特定的状态码。 (为简单起见,可以使用子资源devise,如POST /order/123/canceled了entity-body true 。)

在你的具体情况下,你可能会尝试类似的东西。 这样,当一只狗吠叫时,例如, /v1/dogs/1/GET可以包含该信息(例如<barking>true</barking> 。 或者…如果这太复杂,请放松您的RESTful要求并坚持使用POST

更新:

我不想让这个答案太大,但是需要一段时间才能把一个algorithm(一个动作 )暴露为一组资源。 我们不必从行动的angular度来思考( “在地图上search地点” ),而是需要根据行动的结果( “地图上与search条件匹配的地点列表 )来思考。

如果你发现你的devise不适合HTTP的统一界面,你可能会发现自己回到这一步。

查询variables 作用域信息 ,但表示新的资源( /post?lang=en显然是和/post?lang=jp 相同的资源,只是不同的表示)。 相反,它们用于传递客户端状态 (比如?page=10 ,所以状态不保存在服务器中; ?lang=en也是这里的一个例子)或者input参数algorithm资源/search?q=dogs/dogs?code=1 )。 再次,不是不同的资源。

HTTP动词'(方法)属性:

另一个明确的观点是?action=something URI中的?action=something不是RESTful,是HTTP动词的属性:

  • GETHEAD是安全的(和幂等);
  • PUTDELETE只是幂等的;
  • POST不是。

安全性GETHEAD请求是读取某些数据的请求,而不是更改任何服务器状态的请求。 客户端可以做10次GETHEAD请求,就像做一次一样,或者根本不做

同等效力:一个幂等运算,无论你应用一次还是多次(在math中乘以零是幂等的),都具有相同的效果。 如果您DELETE一次资源,再次删除将具有相同的效果(资源已经是GONE )。

POST既不安全也不幂等。 将两个相同的POST请求发送到“工厂”资源可能会导致两个从属资源包含相同的信息。 重载(方法在URI或实体正文) POST ,所有投注都closures。

这两个属性对于HTTP协议的成功(通过不可靠的networking!)是很重要的:你有多less次更新( GET )页面而不等待它完全加载?

创build一个动作并将其放置在URL中明确地破坏了HTTP方法的合约。 再一次,技术允许你,你可以做到这一点,但这不是REST风格的devise。

我早些时候回答 ,但这个答案与我以前的答案相矛盾,并采取了一个非常不同的策略来解决问题。 它显示了HTTP请求是如何从定义REST和HTTP的概念构build的。 它也使用PATCH而不是POSTPUT

它通过REST约束,然后是HTTP的组件,然后是一个可能的解决scheme。

rest

REST是一组旨在应用于分布式超媒体系统以使其可扩展的约束。 即使要在远程控制某个动作的情况下理解它,也必须考虑将动作作为分布式超媒体系统的一部分进行远程控制 – 这是发现,查看和修改互连信息的一个系统的一部分。 如果这比其价值更麻烦,那么尝试使其成为RESTful可能并不是一件好事。 如果您只想在客户端上使用“控制面板”types的GUI,可以通过端口80触发服务器上的操作,那么您可能需要一个简单的RPC接口,如JSON-RPC,通过HTTP请求/响应或WebSocket。

但是REST是一个非常有趣的思维方式,问题的例子很容易用REST风格的界面进行build模,所以让我们来面对有趣和教育的挑战吧。

REST由四个接口约束来定义 :

资源鉴定; 通过表示操纵资源; 自我描述的信息; 超媒体作为应用程序状态的引擎。

你问如何定义一个界面,满足这些限制,通过哪一台计算机告诉另一台计算机做一个狗叫。 具体而言,您希望您的界面是HTTP,而且您不希望在按预期使用时丢弃使HTTP RESTfulfunction。

让我们从第一个约束开始: 资源识别

任何可以命名的信息可以是资源:文档或图像,时间服务(例如“洛杉矶今天的天气”),其他资源的集合,非虚拟对象(例如,人物)等等。

所以一只狗是一种资源。 它需要被识别。

更确切地说,资源R是时间变化的隶属函数M Rt ),其对于时间t映射到一组实体或等价的值。 集合中的值可以是资源表示和/或资源标识符

你用一组标识符和表示法模拟一只狗,并说在给定的时间它们都是相互关联的。 现在,让我们使用标识符“狗#1”。 这给我们带来了第二个和第三个约束: 资源表示自我描述

REST组件通过使用表示来捕获资源的当前或预期状态,并在组件之间传递该表示,从而对资源执行操作。 表示是一个字节序列,再加上表示元数据来描述这些字节。

以下是捕获犬的预期状态的字节序列,即,我们希望与标识符“狗#1”相关联的表示(注意,它仅代表状态的一部分,因为它不考虑狗的名字,健康,甚至过去的树皮):

自从这种状态发生变化以来,它每10分钟一直在吠叫,并将无限期地持续下去。

它应该被附加到描述它的元数据。 这个元数据可能是有用的:

这是一个英文声明。 它描述了预期状态的一部分。 如果多次收到,只允许第一个有效。

最后,让我们看看第四个约束: HATEOAS

REST …将应用程序视为信息和控制备选scheme的一个紧密结构,用户可以通过该结构执行所需的任务。 例如,在在线字典中查找单词就是一个应用程序,就像通​​过虚拟博物馆游览,或者复习一组课堂笔记来学习考试一样。 …应用程序的下一个控制状态驻留在第一个请求的资源的表示中,因此获得第一个表示是一个优先级。 …因此,模型应用程序是一个通过从当前表示集合中的替代状态转换中检查和select而从一个状态转移到下一个状态的引擎。

在一个RESTful接口中,客户端接收一个资源表示,以便弄清楚它应该如何接收或发送表示。 在应用程序的某个地方必须有一个表示,客户可以从中找出如何接收或发送它应该能够接收或发送的所有表示,即使它遵循一系列表示来获得该信息。 这似乎很简单:

客户要求提供被标识为主页的资源的表示; 作为响应,它得到一个包含客户可能想要的每只狗的标识符的表示。 客户从中提取一个标识符,并询问该服务如何与识别的狗进行交互,服务说客户可以发送描述该狗预期状态的一部分的英语语句。 然后,客户端发送这样的语句,并收到成功消息或错误消息。

HTTP

HTTP实现REST约束如下:

资源标识 :URI

资源表示 :实体主体

自我描述(self-description) :方法或状态代码,头文件,以及可能的实体主体部分(例如XML模式的URI)

HATEOAS :超链接

你已经决定http://api.animals.com/v1/dogs/1作为URI。 假设客户从网站上的某个页面获得了这个信息。

让我们使用这个entity-body( next的值是一个时间戳;当接收到这个请求的时候,值0 '''):

 {"barks": {"next": 0, "frequency": 10}} 

现在我们需要一个方法。 PATCH符合我们决定的“预期状态的一部分”描述:

PATCH方法请求将请求实体中描述的一组变更应用于由Request-URI标识的资源。

还有一些标题:

要指示entity-body的语言: Content-Type: application/json

确保只发生一次: If-Unmodified-Since: <date/time this was first sent>

我们有一个要求:

 PATCH /v1/dogs/1/ HTTP/1.1 Host: api.animals.com Content-Type: application/json If-Unmodified-Since: <date/time this was first sent> [other headers] {"barks": {"next": 0, "frequency": 10}} 

在成功的情况下,客户应该收到一个204状态代码作为回应,或者如果/v1/dogs/1/已经改变以反映新的吠叫时间表,则应该收到205

如果失败,它应该收到一个403和一个有用的消息为什么。

响应GET /v1/dogs/1/ ,对于REST服务来说,反映树皮时间表并不是必需的,但是如果JSON表示包含这一点,这将是最有意义的:

 "barks": { "previous": [x_1, x_2, ..., x_n], "next": x_n, "frequency": 10 } 

将cron作业视为服务器从界面隐藏的实现细节。 这是一个通用接口的美丽。 客户端不必知道服务器在幕后做什么; 所有关心的是该服务理解并响应所请求的状态更改。

大多数人使用POST来达到这个目的。 当没有其他HTTP方法看起来合适时,执行“任何不安全或非等效的操作”是合适的。

诸如XMLRPC的 API使用POST来触发可以运行任意代码的动作。 POST数据中包含“操作”:

 POST /RPC2 HTTP/1.0 User-Agent: Frontier/5.1.2 (WinNT) Host: betty.userland.com Content-Type: text/xml Content-length: 181 <?xml version="1.0"?> <methodCall> <methodName>examples.getStateName</methodName> <params> <param> <value><i4>41</i4></value> </param> </params> </methodCall> 

以RPC为例,说明POST是服务器端方法的HTTP动词的常规select。 这里是关于POST的Roy Fielding的想法 – 他几乎说,使用指定的HTTP方法是RESTful。

请注意,RPC本身不是非常RESTful,因为它不是资源导向的。 但是如果你需要无状态,caching或分层,就不难做出适当的转换。 有关示例,请参阅http://blog.perfectapi.com/2012/opinionated-rpc-apis-vs-restful-apis/

POST是为了devise的HTTP方法

为数据处理过程提供一个数据块

处理非CRUD映射操作的服务器端方法是Roy Fielding与REST一起使用的,所以你在那里很好,这就是为什么POST是非幂等的。 POST将处理大部分数据发布到服务器端方法来处理信息。

也就是说,在你的狗叫情景中,如果你想要每隔10分钟执行一次服务器端的树皮,但由于某种原因,需要触发器来自客户端,因为它的幂等性, PUT将更好地服务于这个目的。 那么严格来说,在这种情况下,多个POST请求没有明显的风险,导致你的狗喵喵叫,但无论如何,这是两个类似的方法的目的。 我对类似SO问题的回答可能对您有用。

如果我们假设Barking是消费者可以采取行动的内部/附属/次要资源,那么我们可以说:

 POST http://api.animals.com/v1/dogs/1/bark 

狗1号树皮

 GET http://api.animals.com/v1/dogs/1/bark 

返回最后的树皮时间戳

 DELETE http://api.animals.com/v1/dogs/1/bark 

不适用! 所以无视它。

早期版本的一些答案build议你使用RPC。 You do not need to look to RPC as it is perfectly possible to do what you want whilst adhering to the REST constraints.

Firstly, don't put action parameters in the URL. The URL defines what you are applying the action to, and query parameters are part of the URL. It should be thought of entirely as a noun. http://api.animals.com/v1/dogs/1/?action=bark is a different resource — a different noun — to http://api.animals.com/v1/dogs/1/ . [nb Asker has removed the ?action=bark URI from the question.] For example, compare http://api.animals.com/v1/dogs/?id=1 to http://api.animals.com/v1/dogs/?id=2 . Different resources, distinguished only by query string. So the action of your request, unless it corresponds directly to a bodyless existing method type (TRACE, OPTIONS, HEAD, GET, DELETE, etc) must be defined in the request body.

Next, decide if the action is " idempotent ", meaning that it can be repeated without adverse effect (see next paragraph for more explanaton). For example, setting a value to true can be repeated if the client is unsure that the desired effect happened. They send the request again and the value remains true. Adding 1 to a number is not idempotent. If the client sends the Add1 command, isn't sure it worked, and sends it again, did the server add one or two? Once you have determined that, you're in a better position to choose between PUT and POST for your method.

Idempotent means a request can be repeated without changing the outcome. These effects do not include logging and other such server admin activity. Using your first and second examples, sending two emails to the same person does result in a different state than sending one email (the recipient has two in their inbox, which they might consider to be spam), so I would definitely use POST for that. If the barkCount in example 2 is intended to be seen by a user of your API or affects something that is client-visible, then it is also something that would make the request non-idempotent. If it is only to be viewed by you then it counts as server logging and should be ignored when determing idempotentcy.

Lastly, determine if the action you want to perform can be expected to succeed immediately or not. BarkDog is a quickly completing action. RunMarathon is not. If your action is slow, consider returning a 202 Accepted , with a URL in the response body for a user to poll to see if the action is complete. Alternatively, have users POST to a list URL like /marathons-in-progress/ and then when the action is done, redirect them from the in progress ID URL to the /marathons-complete/ URL.
For the specific cases #1 and #2, I would have the server host a queue, and the client post batches of addresses to it. The action would not be SendEmails, but something like AddToDispatchQueue. The server can then poll the queue to see if there are any email addresses waiting, and send emails if it finds any. It then updates the queue to indicate that the pending action has now been performed. You would have another URI showing the client the current state of the queue. To avoid double-sending of emails, the server could also keep a log of who it has sent this email to, and check each address against that to ensure it never sends two to the same address, even if you POST the same list twice to the queue.

When choosing a URI for anything, try to think of it as a result, not an action. For example google.com/search?q=dogs shows the results of a search for the word "dogs". It does not necessarilly perform the search.

Cases #3 and #4 from your list are also not idempotent actions. You suggest that the different suggested effects might affect the API design. In all four cases I would use the same API, as all four change the “world state.”

See my new answer — it contradicts this one and explains REST and HTTP more clearly and accurately.

Here's a recommendation that happens to be RESTful but is certainly not the only option. To start barking when the service receives the request:

 POST /v1/dogs/1/bark-schedule HTTP/1.1 ... {"token": 12345, "next": 0, "frequency": 10} 

token is an arbitrary number that prevents redundant barks no matter how many times this request is sent.

next indicates the time of the next bark; a value of 0 means 'ASAP'.

Whenever you GET /v1/dogs/1/bark-schedule , you should get something like this, where t is the time of the last bark and u is t + 10 minutes:

{"last": t, "next": u}

I highly recommend that you use the same URL to request a bark that you use to find out about the dog's current barking state. It's not essential to REST, but it emphasizes the act of modifying the schedule.

The appropriate status code is probably 205 . I'm imagining a client that looks at the current schedule, POST s to the same URL to change it, and is instructed by the service to give the schedule a second look to prove that it has been changed.

说明

rest

Forget about HTTP for a moment. It's essential to understand that a resource is a function that takes time as input and returns a set containing identifiers and representations . Let's simplify that to: a resource is a set R of identifiers and representations; R can change — members can be added, removed, or modified. (Though it's bad, unstable design to remove or modify identifiers.) We say an identifier that is an element of R identifies R , and that a representation that is an element of R represents R .

Let's say R is a dog. You happen to identify R as /v1/dogs/1 . (Meaning /v1/dogs/1 is a member of R .) That's just one of many ways you could identify R . You could also identify R as /v1/dogs/1/x-rays and as /v1/rufus .

How do you represent R ? Maybe with a photograph. Maybe with a set of X-rays. Or maybe with an indication of the date and time when R last barked. But remember that these are all representations of the same resource . /v1/dogs/1/x-rays is an identifier of the same resource that is represented by an answer to the question "when did R last bark?"

HTTP

Multiple representations of a resource aren't very useful if you can't refer to the one you want. That's why HTTP is useful: it lets you connect identifiers to representations . That is, it is a way for the service to receive a URL and decide which representation to serve to the client.

At least, that's what GET does. PUT is basically the inverse of GET : you PUT a representation r at the URL if you wish for future GET requests to that URL to return r , with some possible translations like JSON to HTML.

POST is a looser way of modifying a representation. Think of there being display logic and modification logic that are counterparts to each other — both corresponding to the same URL. A POST request is a request for the modification logic to process the information and modify any representations (not just the representation located by the same URL) as the service sees fit. Pay attention to the third paragraph after 9.6 PUT : you're not replacing the thing at the URL with new content; you're asking the thing at the URL to process some information and intelligently respond in the form of informative representations.

In our case, we ask the modification logic at /v1/dogs/1/bark-schedule (which is the counterpart to the display logic that tells us when it last barked and when it will next bark) to process our information and modify some representations accordingly. In response to future GET s, the display logic corresponding to the same URL will tell us that the dog is now barking as we wish.

Think of the cron job as an implementation detail. HTTP deals in viewing and modifying representations. From now on, the service will tell the client when the dog last barked and when it will bark next. From the service's perspective, that is honest because those times correspond with past and planned cron jobs.

REST is a resource oriented standard, it is not action driven as a RPC would be.

If you want your server to bark , you should look into different ideas like JSON-RPC , or into websockets communication.

Every try to keep it RESTful will fail in my opinion: you can issue a POST with the action parameter, you are not creating any new resources but as you may have side effects, you are safer.