上下文

考虑以下情况:需要创建资源,但操作需要很长时间才能完成。

实际上,这种情况并不少见:毕竟,REST 不是在某些 CRUD 场景中操作几个数据库行。REST是关于任意资源的操作,资源可能需要大量的计算才能存在。

因此,您基本上有两种选择:

  • 您将强制 API 客户端等待资源实际创建

  • 您可以立即返回一些状态响应,并将创建推迟到以后的某个时间点

让我们创建一些不平凡的东西(我使用HTTPie工具):

1± http POST https://api.service.io/stars name='Death Star' 2HTTP/1.1 201 Created 3Location: /stars/12345

在这里,我们试图创造一颗死星,正如你所看到的,它被创造出来并被送回去。Location

现在,正如你可能想象的那样,创造恒星本身并不是一件容易的事,更不用说当我们想要像死星这样装备的东西时了。这意味着我们将不得不等待一段时间才能看到这种反应。201 Created

因此,我们的目标是初始化星形创建,获取确认消息,并享受一些乐趣,直到资源最终准备就绪(如果我们必须不时轮询以检查状态更新,我们就可以了)。

为什么等待并不酷

嗯,因为!

更严重的是,强制API客户端等待本身并没有错。如果在服务器端,你依赖于一些异步循环,并且可以处理疯狂的连接数量,并且如果最终的等待时间是几秒钟(对于你的目的来说是可以接受的),那么你绝对可以停止阅读这篇文章,而不会发现酷孩子已经使用了几年的方法。nn

异步处理(做错了)

你的第一反应可能是:"如果我立即回来,但把实际的创作推迟到以后的某个时间点,那会怎么样?HTTP 201 Created

好吧,你不能那样做。如果您这样做,您将违反HTTP / 1.1:语义和内容协议(更确切地说是RFC 7231的第6.3.2节):

RFC 7231 的 6.3.2 节

201(已创建)状态代码指示请求已得到满足,并已导致创建了一个或多个新资源。请求创建的主要资源由响应中的"位置"标头字段标识,如果未收到"位置"字段,则由有效的请求 URI 标识。

在实际创建资源时必须使用该值,而不是排队等待创建。HTTP 201 Created

异步处理(正确完成)

假设我们有一些队列,我们可以在其中放置长时间运行的作业(由某些工作进程定期执行)。

现在,我们可以返回。201 Created202 Accepted

RFC 6 的 3.3.7231 节

202(已接受)状态代码指示请求已被接受进行处理,但处理尚未完成。

如您所见,这正是我们所追求的!

我们知道要返回什么状态代码,那么标头呢?简单。API 将返回已创建的排队任务的位置,而不是实际资源的位置:Location

1± http POST https://api.service.io/stars name='Death Star' 2HTTP/1.1 202 Accepted 3Location: /queue/12345

:火: 专业提示:允许返回有效负载和响应,您应该利用这个机会返回一些有意义的东西(例如任务完成的ETA,当前状态等)。202 Accepted

实施说明

在延迟处理方面,有几个与实现相关的问题经常被问到。让我们回顾一下它们。

问题 1.如何了解资源何时最终可用?

您需要查询排队的任务(这就是 API 返回标头的原因):Location

1± http GET https://api.service.io/queue/12345 2HTTP/1.1 200 Ok 3 4<response> 5    <status>PENDING</status> 6    <eta>2 mins.</eta> 7    <link rel="cancel" method="delete" href="/queue/12345" /> 8</response>

:火: 专业提示:遵循 HATEOAS 约束,我们可以添加指向允许取消/删除排队任务的状态的链接。

问题 2.创建资源时会发生什么情况,队列任务资源如何更改?

创建资源后,API 应针对排队任务的所有后续请求使用状态代码进行响应:303 See Other

1± http GET https://api.service.io/queue/12345 2HTTP/1.1 303 See Other 3Location: /stars/97865

问题 3.创建完成后,如何处理任务资源?

在创建资源时,其相应的任务可用,您可以向其查询状态。

创建最初所需的资源后,有两种替代方法可以处理临时任务资源:

  • API 客户端必须发出请求,以便服务器清除它。在此之前,服务器将使用状态进行响应。删除后,将返回以供后续请求使用。DELETE303 See Other404 Not FoundGET /queue/12345

  • 或者,垃圾回收可以是服务器的工作:一旦任务完成,服务器可以安全地删除它并使用后续请求进行响应。410 GoneGET /queue/12345

:火: 专业提示:服务器可以为所有新的排队任务分配一些到期日期,并且无论完成状态如何,这些任务都会过期。这样,服务器会限制任何给定任务可以运行的时间(最大值)。这种策略并不违背所宣布的目的,并且允许资源永远不会存在。202 Accepted

引用

  • Thijssen, J. REST 中的异步操作

  • Thijssen, J. REST Cookbook


以上转自:https://farazdagi.com/posts/2014-10-16-rest-long-running-jobs/

以下转自:http://www.ruanyifeng.com/blog/2018/12/async-api-design.html?utm_source=tuicool&utm_medium=referral

网站的前后端通信,往往会有异步请求,这时应该怎么设计 API?

我最近读到一篇文章,作者介绍了他的做法,设计得很精细,我觉得值得借鉴,可以当作异步 API 的标准设计。

一、同步 API

为了便于比较,先看看同步 API 的设计。下面是一个很简单的例子。

客户端发出一个请求,要求创建资源。

POST https://api.service.io/stars

name='Death Star'

服务器回应 201。

HTTP/1.1 201 Created
Location: /stars/12345

201 Created告诉客户端,请求成功,资源已经创建。新的资源的网址请看Location字段。

二、异步请求

如果服务器不能立即返回结果,就形成了异步操作。

客户端的请求还是一样的。

POST https://api.service.io/stars

name='Death Star'

服务器回应 202。

HTTP/1.1 202 Accepted
Location: /queue/12345

202 Accepted告诉客户端,请求已经接受,但还没有处理,可以去Location字段查询进展。

除了上面的头信息,服务器的回应如果有数据体,可以返回一些有效信息(比如任务完成的估计时间、当前状态等等)。

三、查询进展

过了一段时间,客户端就发出请求,查询异步处理的进展。

GET https://api.service.io/queue/12345

服务器回应 200。

HTTP/1.1 200 Ok  

<response>
 <status>PENDING</status>
  <eta>2 mins.</eta>
  <link rel="cancel" method="delete" href="/queue/12345" /> 
</response>

200 Ok告诉客户端,请求成功,具体情况查看数据体。数据体里给出提示,异步操作已成功或还需要等待。

四、异步操作成功

有一种特殊情况,用户查询异步操作的进展的时候,可能会希望,如果异步操作已经完成,就直接跳转到新资源。

这时,服务器回应 303。

HTTP/1.1 303 See Other 
Location: /stars/97865

303 see other告诉客户端,重定向到不同的资源。Location字段就是跳转的目标,也就是新资源的网址。

五、删除查询链接

一旦异步操作完成,客户端可以要求服务器删除查询链接。

DELETE https://api.service.io/queue/12345

服务器回应 204。

HTTP/1.1 204 No Content

204 No Content告诉客户端,删除成功。以后,客户端再访问这个查询链接,服务器回应404 Not Found

如果客户端不删除查询链接,服务器完成异步任务后,也可以自动删除。客户端再请求这个链接,服务器回应410 Gone,表示该链接永久性不再可用。

(完)