请记住,我对REST有基本的了解。假设我有这个网址:
http://api.animals.com/v1/dogs/1/
现在,我想让服务器让狗吠。只有服务器知道如何执行此操作。假设我想让它在一个CRON工作上运行,这使得狗在永恒的剩余时间内每隔10分钟就会吠叫一次。这个电话是什么样的?我有点想这样做:
网址请求:
ACTION http://api.animals.com/v1/dogs/1/
在请求正文中:
{"action":"bark"}
在你为我编写自己的HTTP方法而生气之前,请帮助我,让我更好地了解如何以RESTful方式调用服务器端方法。 :)
编辑澄清
更多关于“树皮”方法的说明。以下是一些可能导致不同结构化API调用的选项:
答案 0 :(得分:6)
我answered earlier,但这个答案与我的旧答案相矛盾,并采用了一种不同的策略来实现解决方案。它显示了如何根据定义REST的概念构建HTTP请求和HTTP。它还使用PATCH
代替POST
或PUT
。
它通过REST约束,然后是HTTP的组件,然后是一个可能的解决方案。
REST是一组旨在应用于分布式超媒体系统的约束,以使其可扩展。即使在远程控制动作的上下文中理解它,您也必须考虑将动作远程控制为分布式超媒体系统的一部分 - 分布式超媒体系统是发现,查看和修改互连信息的系统的一部分。如果这比它的价值更麻烦,那么尝试使其成为RESTful可能并不好。如果你只是想要一个"控制面板"在客户端上键入GUI,可以通过端口80触发服务器上的操作,然后您可能需要一个简单的RPC接口,如通过HTTP请求/响应或WebSocket的JSON-RPC。
但REST是一种引人入胜的思维方式,问题中的例子很容易通过RESTful界面进行建模,所以让我们接受有趣和教育的挑战。
REST是defined四个接口约束:
资源识别;通过陈述来处理资源;自我描述性的信息;并且,超媒体作为应用程序状态的引擎。
你问你如何定义一个接口,满足这些约束,一台计算机通过它告诉另一台计算机让狗叫。具体来说,您希望您的界面是HTTP,并且您不希望丢弃使用HTTP RESTful的功能。
让我们从第一个约束开始:资源识别。
任何可以命名的信息都可以是资源:文档或图像,时间服务(例如"今天洛杉矶的天气"),其他资源的集合,非 - 虚拟对象(例如一个人),等等。
所以狗是一种资源。需要确定它。
更确切地说,资源 R 是时间上变化的隶属函数 M R ( t ),其中对于时间 t 映射到一组实体或值,它们是等价的。集合中的值可以是资源表示和/或资源标识符。
通过获取一组标识符和表示形式并说明它们在给定时间彼此关联,您建模狗。现在,让我们使用标识符" dog#1"。这带来了第二和第三个约束:资源代表和自我描述。
REST组件通过使用表示来捕获资源的当前或预期状态并在组件之间传输该表示,从而对资源执行操作。表示是一个字节序列,加上用于描述这些字节的表示元数据。
以下是捕获狗的预期状态的字节序列,即我们希望与标识符相关联的表示"狗#1" (请注意,它只代表州的一部分,因为它不考虑狗的名字,健康,甚至过去的吠声):
自从州改变生效以来,它每10分钟一直在吠叫,并将无限期地持续下去。
它应该附加到描述它的元数据。此元数据可能很有用:
这是一份英文声明。它描述了预期状态的一部分。如果多次收到,只允许第一个产生效果。
最后,让我们看看第四个约束: HATEOAS 。
REST ...将应用程序视为信息和控制备选方案的一致结构,用户可以通过该结构执行所需任务。例如,在在线词典中查找单词是一个应用程序,如在虚拟博物馆中巡回演出,或者查看一组课堂笔记来学习考试。 ...应用程序的下一个控制状态驻留在第一个请求资源的表示中,因此获得第一个表示是优先级。 ...因此,模型应用程序是一个引擎,它通过检查当前表示集中的备用状态转换并从中选择,从一个状态移动到下一个状态。
在RESTful接口中,客户端接收资源表示以便弄清楚它应该如何接收或发送表示。在应用程序的某处必须有一个表示,客户端可以从中找出如何接收或发送它应该能够接收或发送的所有表示,即使它遵循一系列表示来获得该信息。这看起来很简单:
客户要求表示被识别为主页的资源;作为响应,它获得一个表示,其中包含客户端可能想要的每只狗的标识符。客户端从中提取标识符并询问服务如何与识别的狗进行交互,并且服务说客户端可以发送描述狗的预期状态的部分英语声明。然后客户端发送这样的语句并收到成功消息或错误消息。
HTTP实现REST约束,如下所示:
资源标识:URI
资源代表:entity-body
自我描述:方法或状态代码,标头以及实体主体的可能部分(例如XML架构的URI)
HATEOAS :超链接
您已决定使用http://api.animals.com/v1/dogs/1
作为URI。让我们假设客户从网站的某个页面获得了这个。
让我们使用此实体 - 正文(next
的值是时间戳;值0
表示&#39;收到此请求时&#39;):< / p>
{"barks": {"next": 0, "frequency": 10}}
现在我们需要一种方法。 PATCH符合预期状态&#34;的一部分。描述我们决定:
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作业视为服务器从界面隐藏的实现细节。这是通用界面的美妙之处。客户端不必知道服务器在幕后做了什么;所有它关心的是服务理解并响应所请求的状态变化。
答案 1 :(得分:3)
大多数人为此目的使用 POST 。当没有其他HTTP方法看起来合适时,它适用于执行任何不安全或无效的操作&#34;。
XMLRPC等API使用 POST 来触发可以运行任意代码的操作。 &#34;动作&#34;包含在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 thoughts on POST - 他几乎说使用指定的HTTP方法是RESTful。
请注意,RPC本身并不是非常RESTful的,因为它不是面向资源的。但是,如果您需要无状态,缓存或分层,那么进行适当的转换并不困难。有关示例,请参阅http://blog.perfectapi.com/2012/opinionated-rpc-apis-vs-restful-apis/。
答案 2 :(得分:2)
POST
是<{p>的HTTP method designed
为数据处理流程提供数据块
处理非CRUD映射操作的服务器端方法是使用REST的Roy Fielding intended,所以你很擅长,这就是为什么POST
是非幂等的。 POST
将处理大多数数据发布到服务器端方法以处理信息。
那就是说,在您的狗吠情景中,如果您希望每10分钟执行一次服务器端树皮,但由于某种原因需要触发器来自客户端,PUT
将用于此目的更好,因为它的幂等性。好吧,严格来说,这种情况没有明显的多次POST请求导致你的狗喵喵叫的风险,但无论如何这是两种类似方法的目的。 My answer to a similar SO question可能对您有用。
答案 3 :(得分:1)
某些答案的早期修订建议您使用RPC。您不需要查看RPC,因为 完全可以在遵守REST约束的情况下完成您想要的操作。
首先,不要在URL中放置动作参数。 URL定义您正在应用操作的,查询参数是URL的一部分。它应该完全被认为是一个名词。 http://api.animals.com/v1/dogs/1/?action=bark
是一个不同的资源 - 一个不同的名词 - 到http://api.animals.com/v1/dogs/1/
。 [注: 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等)。
接下来,确定该动作是否为“idempotent”,这意味着它可以重复而没有不利影响(有关更多说明,请参阅下一段)。例如,如果客户端不确定发生了所需的效果,则可以重复将值设置为true。他们再次发送请求,值仍然为真。将1添加到数字不是幂等的。如果客户端发送Add1命令,不确定它是否有效,并再次发送,服务器是否添加了一个或两个?确定后,您可以更好地在PUT
和POST
之间进行选择。
幂等意味着可以在不改变结果的情况下重复请求。这些效果不包括日志记录和其他此类服务器管理活动。使用您的第一个和第二个示例,向同一个人发送两封电子邮件会导致与发送一封电子邮件不同的状态(收件人在收件箱中有两封电子邮件,他们可能认为这是垃圾邮件),所以我肯定会使用POST 。如果示例2中的barkCount旨在被API的用户看到或影响客户端可见的内容,那么它也会使请求不是幂等的。如果它只是被您查看,那么它将被视为服务器日志记录,在确定幂等性时应该被忽略。
最后,确定您希望执行的操作是否可以立即成功。 BarkDog是一个快速完成的行动。 RunMarathon不是。如果您的操作很慢,请考虑返回202 Accepted
,并在响应正文中包含一个URL,供用户轮询以查看操作是否完整。或者,让用户POST到/marathons-in-progress/
之类的列表网址,然后在操作完成后,将其从正在进行的ID网址重定向到/marathons-complete/
网址。
对于特定情况#1和#2,我将让服务器主持一个队列,并且客户端将批量地址发布到它。该操作不是SendEmails,而是类似AddToDispatchQueue。然后,服务器可以轮询队列以查看是否有任何电子邮件地址等待,并在发现任何电子邮件时发送电子邮件。然后,它会更新队列以指示现在已执行挂起操作。您将有另一个URI显示客户端队列的当前状态。为了避免重复发送电子邮件,服务器还可以记录发送此电子邮件的人员,并检查每个地址,以确保它永远不会将两个地址发送到同一地址,即使您将相同的列表两次发送到队列。
为任何事物选择URI时,请将其视为结果,而不是动作。例如,google.com/search?q=dogs
显示搜索单词“dogs”的结果。它没有必要执行搜索。
列表中的案例#3和#4也不是幂等行为。您建议不同的建议效果可能会影响API设计。在所有四种情况下,我都会使用相同的API,因为所有四种情况都会改变“世界状态”。
答案 4 :(得分: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
不适用!所以忽略它。
答案 5 :(得分:0)
查看我的new answer - 它与此相矛盾,更清晰准确地解释了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 是最后一次吠叫的时间而你是 t + 10分钟:
{"last": t, "next": u}
我强烈建议您使用相同的URL来请求用于了解狗当前吠叫状态的树皮。它不是REST必不可少的,但它强调修改计划的行为。
相应的状态代码可能是205。我想象一个客户端查看当前的日程安排,POST
到相同的URL进行更改,并且服务人员指示他们再次查看日程表以证明它已被更改。
暂时忘掉HTTP。理解resource是一个需要时间作为输入并返回包含标识符和表示的集合的函数是至关重要的。让我们简化为:资源是标识符和表示的集合 R ; R 可以更改 - 可以添加,删除或修改成员。 (虽然删除或修改标识符是不好的,不稳定的设计。)我们说作为 R 元素的标识符标识 R ,并且表示是一个元素 R 代表 R 。
让我们说 R 是一只狗。您碰巧将 R 标识为/v1/dogs/1
。 (含义/v1/dogs/1
是 R 的成员。)这只是您可以识别 R 的众多方法之一。您还可以将 R 标识为/v1/dogs/1/x-rays
和/v1/rufus
。
您如何表示 R ?也许带着照片。也许用一组X射线。或者可能指示 R 最后一次咆哮的日期和时间。但请记住,这些都是相同资源的所有表示。 /v1/dogs/1/x-rays
是同一资源的标识符,该问题由“ R 上次发布的时间?”问题的答案表示。
如果您无法引用所需的资源,则多个资源表示形式不是很有用。这就是HTTP有用的原因:它让你connect identifiers to representations。也就是说,这是服务接收URL并决定向客户端提供哪种表示的一种方式。
至少,这就是GET
的作用。 PUT
基本上与GET
相反:如果您希望将来PUT
个网址的GET
请求,则POST
表示 r 返回 r ,其中包含一些可能的翻译,例如JSON到HTML。
/v1/dogs/1/bark-schedule
是一种修改表示的宽松方式。可以想象存在彼此对应的显示逻辑和修改逻辑 - 两者都对应于相同的URL。 POST请求是对修改逻辑的请求,以处理信息并修改服务认为合适的任何表示(不仅仅是由同一URL定位的表示)。注意9.6 PUT之后的第三段:你不是用新内容替换URL上的东西;你要求URL上的东西处理一些信息,并以信息表示的形式进行智能回应。
在我们的例子中,我们要求GET
处的修改逻辑(它是显示逻辑的对应部分,它告诉我们它最后一次吠叫以及何时接下来会发出声音)来处理我们的信息并相应地修改一些表示。为了回应未来{{1}} s,对应于同一URL的显示逻辑将告诉我们狗现在正在按照我们的意愿吠叫。
将cron作业视为实现细节。 HTTP处理查看和修改表示。从现在开始,该服务将告诉客户当狗最后一次吠叫以及它何时会吠叫。从服务的角度来看,这是诚实的,因为那些时间与过去和计划的cron工作相对应。
答案 6 :(得分:-1)
REST是一种面向资源的标准,它不像RPC那样是动作驱动的。
如果您希望服务器 bark ,您应该查看不同的想法,例如JSON-RPC,或者进入websockets通信。
在我看来,每次尝试保持RESTful都会失败:您可以使用POST
参数发出action
,您没有创建任何新资源但是因为您可能有副作用,更安全。