160

Keep in mind I have a rudimentary understanding of REST. Let's say I have this URL:

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

And now, I want to make the server make the dog bark. Only the server knows how to do this. Let's say I want to have it run on a CRON job that makes the dog bark every 10 minutes for the rest of eternity. What does that call look like? I kind of want to do this:

URL request:

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

In the request body:

{"action":"bark"}

Before you get mad at me for making up my own HTTP method, help me out and give me a better idea on how I should invoke a server-side method in a RESTful way. :)

EDIT FOR CLARIFICATION

Some more clarification around what the "bark" method does. Here are some options that may result in differently structured API calls:

  1. bark just sends an email to dog.email and records nothing.
  2. bark sends an email to dog.email and the increments dog.barkCount by 1.
  3. bark creates a new "bark" record with bark.timestamp recording when the bark occured. It also increments dog.barkCount by 1.
  4. bark runs a system command to pull the latest version of the dog code down from Github. It then sends a text message to dog.owner telling them that the new dog code is in production.
4

8 回答 8

301

为什么要以 RESTful 设计为目标?

RESTful 原则为 Web 服务 API 设计带来了使网站变得容易(让随机的人类用户“冲浪”它们)的特性,因此它们对程序员来说很容易使用。REST 不好,因为它是 REST,它很好,因为它很好。它很好,主要是因为它很简单

普通 HTTP 的简单性(没有 SOAP 信封和单 URI 重载POST服务),有些人可能称之为“缺乏功能”,实际上是它最大的优势。马上,HTTP 要求您具有可寻址性和无状态性:这两个基本设计决策使 HTTP 可扩展到当今的大型站点(和大型服务)。

但 REST 并不是万能的:有时 RPC 样式(“远程过程调用” - 例如 SOAP)可能是合适的,有时其他需求优先于 Web 的优点。这可以。我们真正不喜欢的是不必要的复杂性。程序员或公司经常引入 RPC 样式的服务来完成普通旧 HTTP 可以处理的工作。其效果是 HTTP 被简化为用于解释“真正”发生的事情的巨大 XML 有效负载的传输协议(不是 URI 或 HTTP 方法提供有关它的线索)。生成的服务过于复杂,无法调试,并且除非您的客户具有开发人员预期的确切设置,否则将无法正常工作。

同样,Java/C# 代码不能是面向对象的,仅使用 HTTP 不会使设计成为 RESTful 人们可能会急于根据操作和应该调用的远程方法来考虑他们的服务。难怪这将主要以 RPC 风格的服务(或 REST-RPC 混合)结束。第一步是换个角度思考。RESTful 设计可以通过多种方式实现,一种方式是从资源而非操作的角度来考虑您的应用程序:

而不是考虑它可以执行的操作(“在地图上搜索地点”)......

...尝试根据这些操作的结果进行思考(“地图上与搜索条件匹配的地点列表”)。

我将在下面举例。(REST 的另一个关键方面是 HATEOAS 的使用——这里我不刷它,但我会在另一篇文章中快速谈论它。)


第一个设计的问题

让我们看一下建议的设计:

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

首先,我们不应该考虑创建一个新的 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/是一个“文档处理器”(类似工厂的)URI。“文档处理器”是一个 URI,您只需“扔东西”并“忘记”它们 - 处理器可能会在“扔东西”之后将您重定向到新创建的资源。例如,用于在消息代理服务上发布消息的 URI,在发布后会将您重定向到显示消息处理状态的 URI。

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

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

所以我们现在所知道的是,上面的设计并不是那么 RESTful,但那到底是什么?它有什么不好?基本上,这很糟糕,因为这是具有复杂含义的复杂 URI。你无法从中推断出任何东西。程序员怎么会知道狗有一个bark动作可以秘密地注入POST它呢?


设计问题的 API 调用

因此,让我们切入正题,尝试从资源方面考虑以RESTful 的方式设计这些树皮。请允许我引用Restful Web Services书:

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

根据上面的描述,我们可以看到bark可以建模为a 的子资源dog(因为 abark包含在狗中,也就是说,吠叫狗“吠”)。

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

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

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

##1。bark 只是发送了一封电子邮件,dog.email没有记录任何内容。

首先,吠叫(发送电子邮件)是同步任务还是异步任务?其次,bark请求是否需要任何文件(可能是电子邮件)还是空的?


1.1 bark 发邮件到dog.email什么都不记录(作为同步任务)

这个案例很简单。对工厂资源的调用会立即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

因为它没有记录(更改)任何内容,200 OK就足够了。它表明一切都按预期进行。


1.2 bark 发邮件到dog.email什么都不记录(作为异步任务)

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

POST /v1/dogs/1/barks HTTP/1.1
Host: api.animals.com
Authorization: Basic mAUhhuE08u724bh249a2xaP=

{document body, if needed;
NOTE: when possible, the response SHOULD contain a short hypertext note with a hyperlink
to the newly created resource (bark) URI, the same returned in the Location header
(also notice that, for the 202 status code, the Location header meaning is not
standardized, thus the importance of a hipertext/hyperlink response)}

202 Accepted
Location: http://api.animals.com/v1/dogs/1/barks/a65h44

这样,每一个bark都是可追溯的。然后,客户端可以向 URI 发出 aGETbark了解其当前状态。甚至可以使用 aDELETE来取消它。


2. bark 发送电子邮件至dog.email,然后递增dog.barkCount1

如果您想让客户端知道dog资源已更改,这可能会更棘手:

POST /v1/dogs/1/barks HTTP/1.1
Host: api.animals.com
Authorization: Basic mAUhhuE08u724bh249a2xaP=

{document body, if needed; when possible, containing a hipertext/hyperlink with the address
in the Location header -- says the standard}

303 See Other
Location: http://api.animals.com/v1/dogs/1

在这种情况下,location标头的目的是让客户知道他应该查看dog. 来自HTTP RFC 关于303

此方法的存在主要是为了允许 POST-activate 脚本的输出将用户代理重定向到选定的资源。

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


3. bark在 bark 发生时创建一个bark带有记录的新“ ”记录。bark.timestamp它也以 1 递增dog.barkCount

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/barks/a65h44

在这里,bark由于请求而创建,因此201 Created应用状态。

如果创建是异步的,202 Accepted则需要 a (如 HTTP RFC 所述)。

保存的时间戳是bark资源的一部分,可以用GET它来检索。更新后的狗也可以在其中“记录” GET dogs/X/barks/Y


4. bark 运行系统命令从 Github 拉取最新版本的狗代码。然后它会发送一条短信dog.owner告诉他们新的狗代码正在生产中。

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

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/barks/a65h44

然后客户端会发出GETs 来/v1/dogs/1/barks/a65h44了解当前状态(如果代码被提取,则电子邮件已发送给所有者等)。每当狗改变时,a303是适用的。


包起来

引用罗伊菲尔丁的话

REST 对方法的唯一要求是它们必须为所有资源统一定义(即,中介不必知道资源类型即可理解请求的含义)。

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

程序员会知道: aPOST产生barksa bark。响应状态代码(必要时还带有实体主体和标题)可以解释发生了什么变化以及客户端可以和应该如何继续。

注意:使用的主要来源是:“ Restful Web Services ”一书、HTTP RFCRoy Fielding 的博客




编辑:

自首次创建以来,问题和答案已经发生了很大变化。最初的问题询问了 URI 的设计,例如:

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

以下是为什么它不是一个好选择的解释:

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

  • RESTful Web 服务在 HTTP 方法中传递方法信息。
  • 典型的 RPC 样式和 SOAP 服务将它们保存在实体主体和 HTTP 标头中。

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

  • RESTful 服务使用 URI。SOAP/RPC 样式服务再次使用实体主体和 HTTP 标头。

以 Google 的 URI 为例http://www.google.com/search?q=DOG。在那里,方法信息是GET,作用域信息是/search?q=DOG

长话短说:

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

和经验法则:

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

您可以将“树皮” “动作”放在 URL(或实体正文中)并使用POST. 没问题,它可以工作,并且可能是最简单的方法,但这不是 RESTful

为了使您的服务真正保持 RESTful,您可能必须退后一步,考虑一下您在这里真正想要做什么(它会对资源产生什么影响)。

我无法谈论您的具体业务需求,但让我举个例子:考虑一个 RESTful 订购服务,其中订单位于 URI 之类的example.com/order/123.

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

正如我们上面所说,这不是 RESTful。相反,我们可能会使用发送到的元素PUT的新表示形式:ordercanceledtrue

PUT /order/123 HTTP/1.1
Content-Type: application/xml

<order id="123">
    <customer id="89987">...</customer>
    <canceled>true</canceled>
    ...
</order>

就是这样。如果订单无法取消,可以返回特定的状态码。(为简单起见,也可以使用POST /order/123/canceled与实体主体类似的子资源设计。)true

在您的特定情况下,您可以尝试类似的方法。这样,例如,当狗在吠叫时,GETat/v1/dogs/1/可以包含该信息(例如<barking>true</barking>。或者......如果这太复杂了,放宽你的 RESTful 要求并坚持使用POST.

更新:

我不想让答案太大,但是需要一段时间才能掌握将算法(一个动作)公开为一组资源的窍门。与其从行动的角度思考(“在地图上搜索地点”),不如从行动的结果(“地图上与搜索条件匹配的地点列表”)的角度思考。

如果您发现您的设计不适合 HTTP 的统一接口,您可能会发现自己回到了这一步。

查询变量 范围信息,但表示新资源(/post?lang=en显然是与 相同的资源/post?lang=jp,只是表示不同)。相反,它们用于传达客户端状态(如?page=10,因此状态不会保存在服务器中;?lang=en这里也是一个示例)或算法资源输入参数(, )。同样,不是不同的资源。/search?q=dogs/dogs?code=1

HTTP 动词的(方法)属性:

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

  • GET并且HEAD是安全的(并且是幂等的);
  • PUT并且DELETE仅是幂等的;
  • POST也不是。

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

幂等性:一种幂等操作,无论您应用一次还是多次应用它都具有相同的效果(在数学中,乘以零是幂等的)。如果你DELETE一个资源一次,再次删除将具有相同的效果(资源GONE已经)。

POST既不安全也不幂等。向“工厂”资源发出两个相同POST的请求可能会导致两个从属资源包含相同的信息。使用重载(URI 或实体主体中的方法)POST,所有的赌注都没有了。

这两个属性对于 HTTP 协议的成功都很重要(通过不可靠的网络!):您有多少次更新(GET)页面而不等到它完全加载?

创建一个动作并将其放在 URL 中显然违反了 HTTP 方法的约定。再一次,技术允许你,你可以做到,但这不是 RESTful 设计。

于 2013-06-02T00:04:50.250 回答
6

之前回答过,但是这个答案与我的旧答案相矛盾,并且遵循了一种截然不同的解决方案。它展示了如何根据定义 REST 和 HTTP 的概念构建 HTTP 请求。它还使用PATCH代替POSTor PUT

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

休息

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

但是 REST 是一种令人着迷的思维方式,并且问题中的示例恰好很容易使用 RESTful 接口进行建模,所以让我们为了乐趣和教育而接受挑战。

REST由四个接口约束定义:

资源识别;通过表示来操纵资源;自我描述的信息;并且,超媒体作为应用程序状态的引擎。

你问如何定义一个接口,满足这些约束,一台计算机通过它告诉另一台计算机让狗叫。具体来说,您希望您的接口是 HTTP,并且您不想丢弃在按预期使用时使 HTTP 成为 RESTful 的特性。

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

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

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

更准确地说,资源R是一个随时间变化的隶属函数M R ( t ),它在时间t映射到一组等效的实体或值。集合中的值可以是资源表示和/或资源标识符

您通过获取一组标识符和表示并说它们在给定时间都相互关联来对狗进行建模。现在,让我们使用标识符“dog #1”。这给我们带来了第二个和第三个约束:资源表示自我描述

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

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

自此状态更改生效以来,它每 10 分钟就开始吠叫一次,并将无限期地持续下去。

它应该附加到描述它的元数据上。此元数据可能有用:

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

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

REST ... 将应用程序视为信息和控制选项的内聚结构,用户可以通过它执行所需的任务。例如,在在线词典中查找一个单词就是一个应用程序,就像参观虚拟博物馆或复习一组课堂笔记以备考一样。...应用程序的下一个控制状态驻留在第一个请求资源的表示中,因此获得第一个表示是优先级。...因此,模型应用程序是一个引擎,它通过检查并从当前表示集中的替代状态转换中进行选择,从一个状态移动到下一个状态。

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

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

HTTP

HTTP 实现 REST 约束如下:

资源标识:URI

资源表示:实体主体

self-description:方法或状态码、标头以及可能的实体主体部分(例如 XML 模式的 URI)

HATEOAS : 超链接

您已决定http://api.animals.com/v1/dogs/1作为 URI。让我们假设客户从网站上的某个页面得到这个。

让我们使用这个实体主体(值next是时间戳;值0表示“收到此请求时”):

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

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

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

还有一些标题:

指示实体主体的语言: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状态码作为响应,或者205如果 的表示/v1/dogs/1/已更改以反映新的吠叫时间表。

失败时,它应该收到403一个有用的消息,原因是什么。

对于 REST,服务不必在响应中反映树皮时间表GET /v1/dogs/1/,但如果 JSON 表示包含以下内容,则最有意义:

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

将 cron 作业视为服务器对接口隐藏的实现细节。这就是通用接口的美妙之处。客户端不必知道服务器在幕后做了什么;它所关心的只是服务理解并响应请求的状态更改。

于 2013-06-10T03:54:30.663 回答
4

大多数人为此目的使用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 动词的常规选择。这是Roy Fielding 关于 POST 的想法——他几乎说使用指定的 HTTP 方法是 RESTful。

请注意,RPC 本身并不是非常 RESTful,因为它不是面向资源的。但是,如果您需要无状态、缓存或分层,则进行适当的转换并不难。有关示例,请参见http://blog.perfectapi.com/2012/opinionated-rpc-apis-vs-restful-apis/ 。

于 2013-06-01T22:45:26.063 回答
2

POST是设计用于的HTTP 方法

向数据处理过程提供数据块...

处理非 CRUD 映射操作的服务器端方法是Roy Fielding对 REST 的预期,所以你很好,这就是为什么POST要成为非幂等的。POST将处理大多数将数据发布到服务器端方法来处理信息。

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

于 2013-06-04T17:53:35.887 回答
1

一些答案的早期版本建议您使用 RPC。您不需要查看 RPC,因为它完全有可能在遵守 REST 约束的同时做您想做的事情。

首先,不要将操作参数放在 URL 中。URL 定义您要应用操作的对象,查询参数是 URL 的一部分。它应该完全被认为是一个名词。 http://api.animals.com/v1/dogs/1/?action=bark与 .是不同的资源——不同的名词http://api.animals.com/v1/dogs/1/。[nb Asker 已从?action=bark问题中删除了 URI。] 例如,http://api.animals.com/v1/dogs/?id=1比较http://api.animals.com/v1/dogs/?id=2. 不同的资源,仅通过查询字符串区分。因此,除非它直接对应于无主体的现有方法类型(TRACE、OPTIONS、HEAD、GET、DELETE 等),否则您的请求的操作必须在请求主体中定义。

接下来,确定该动作是否是“幂等的”,这意味着它可以重复而不会产生不利影响(更多解释请参见下一段)。例如,如果客户不确定预期的效果是否发生,则可以重复将值设置为 true。他们再次发送请求并且值保持为真。给一个数加 1 不是幂等的。如果客户端发送 Add1 命令,不确定它是否有效,然后再次发送,服务器添加了一个还是两个?一旦您确定了这一点,您就可以更好地在您的方法之间进行PUT选择。POST

幂等意味着可以重复请求而不改变结果。这些影响不包括日志记录和其他此类服务器管理活动。使用您的第一个和第二个示例,向同一个人发送两封电子邮件确实会导致与发送一封电子邮件不同的状态(收件人的收件箱中有两个,他们可能认为是垃圾邮件),所以我肯定会使用 POST . 如果示例 2 中的 barkCount 旨在让您的 API 用户看到或影响客户端可见的某些内容,那么它也会使请求成为非幂等的。如果它仅供您查看,则它被视为服务器日志记录,在确定幂等性时应忽略。

最后,确定您要执行的操作是否可以立即成功。BarkDog 是一个快速完成的动作。RunMarathon 不是。如果您的操作很慢,请考虑202 Accepted在响应正文中返回带有 URL 的 , 供用户轮询以查看操作是否完成。或者,让用户 POST 到类似的列表 URL /marathons-in-progress/,然后在操作完成后,将他们从正在进行的 ID URL 重定向到/marathons-complete/URL。
对于特定情况 #1 和 #2,我将让服务器托管一个队列,然后客户端将成批的地址发布给它。该操作不会是 SendEmails,而是类似于 AddToDispatchQueue。然后,服务器可以轮询队列以查看是否有任何电子邮件地址在等待,如果找到则发送电子邮件。然后它更新队列以指示挂起的操作现在已经执行。您将有另一个 URI 向客户端显示队列的当前状态。为避免重复发送电子邮件,服务器还可以记录它向谁发送了这封电子邮件,并检查每个地址以确保它永远不会将两个电子邮件发送到同一个地址,即使你两次发布相同的列表到队列。

在为任何事物选择 URI 时,请尝试将其视为结果,而不是操作。例如google.com/search?q=dogs显示搜索单词“dogs”的结果。它不一定执行搜索。

您列表中的案例#3 和#4 也不是幂等操作。您认为不同的建议效果可能会影响 API 设计。在所有四种情况下,我都会使用相同的 API,因为所有四种都会更改“世界状态”。</p>

于 2013-06-02T15:15:09.747 回答
1

如果我们假设 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

不适用!所以忽略它。

于 2013-06-05T12:52:57.013 回答
0

请参阅我的新答案——它与这个答案相矛盾,并更清楚、更准确地解释了 REST 和 HTTP。

这是一个恰好是 RESTful的建议,但肯定不是唯一的选择。当服务收到请求时开始吠叫:

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

token是一个任意数字,无论此请求发送多少次,都可以防止出现多余的吠叫。

next指示下一次吠叫的时间;值0表示“尽快”。

每当你GET /v1/dogs/1/bark-schedule,你应该得到这样的东西,其中t是最后一次吠叫的时间,ut + 10 分钟:

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

我强烈建议您使用相同的 URL 来请求您用来了解狗当前吠叫状态的吠叫。这对 REST 来说不是必需的,但它强调了修改计划的行为。

适当的状态码可能是205。我正在想象一个客户端查看当前的日程安排,POST访问相同的 URL 来更改它,并由服务指示再次查看日程安排以证明它已被更改。

解释

休息

暂时忘记 HTTP。必须理解资源是一个函数,它需要时间作为输入并返回一个包含标识符表示的集合。让我们将其简化为:资源是一组标识符和表示形式;R可以更改——可以添加、删除或修改成员。(尽管删除或修改标识符是不好的、不稳定的设计。)我们说作为 R 元素的标识符标识R,并且作为 R 元素的表示表示R

假设R是一只狗。您碰巧将R标识为/v1/dogs/1。(意思/v1/dogs/1R的成员。)这只是您可以识别R的众多方法之一。您还可以将R标识为/v1/dogs/1/x-rays/v1/rufus

你如何表示R?也许有照片。也许用一组 X 光片。或者可能带有R最后一次吠叫的日期和时间的指示。但请记住,这些都是同一资源的所有表示。是同一资源的标识符,由对“ R最后一次吠叫是什么/v1/dogs/1/x-rays时候?”问题的答案表示。

HTTP

如果您无法引用所需的资源,则资源的多个表示不是很有用。这就是 HTTP 有用的原因:它允许您将标识符连接到表示。也就是说,它是服务接收 URL 并决定向客户端提供哪种表示的一种方式。

至少,是GET这样的。PUT基本上是r 的反面GET:如果您希望将来对该 URL 的请求返回rPUT ,则您在 URL 处表示r,并带有一些可能的翻译,例如 JSON 到 HTML。GET

POST是修改表示的一种更宽松的方式。考虑一下显示逻辑和修改逻辑,它们是彼此对应的——它们都对应于同一个 URL。POST 请求是对修改逻辑的请求,以处理信息并修改服务认为合适的任何表示(不仅仅是位于同一 URL 的表示)。注意9.6 PUT之后的第三段:你没有用新内容替换 URL 处的东西;您要求 URL 处的事物处理一些信息并以信息表示的形式智能地响应。

在我们的例子中,我们要求修改逻辑/v1/dogs/1/bark-schedule(它是显示逻辑的对应部分,它告诉我们它最后一次吠叫什么时候以及下一次吠叫什么时候)来处理我们的信息并相应地修改一些表示。响应futures GET,同一个URL对应的显示逻辑会告诉我们,狗现在正在如我们所愿地吠叫。

将 cron 作业视为实现细节。HTTP 处理查看和修改表示。从现在开始,该服务将告诉客户狗最后一次吠叫的时间以及下一次吠叫的时间。从服务的角度来看,这是诚实的,因为这些时间与过去和计划的 cron 作业相对应。

于 2013-06-08T08:23:23.650 回答
-1

REST 是一种面向资源的标准,它不像 RPC 那样由动作驱动。

如果你想让你的服务器狂吠,你应该研究不同的想法,比如JSON-RPC,或者 websockets 通信。

在我看来,每次尝试保持 RESTful 都会失败:您可以POST使用action参数发出 a,您不会创建任何新资源,但由于您可能会产生副作用,因此您会更安全。

于 2013-06-01T22:49:50.040 回答