异步 API 的设计

作者: 阮一峰

日期: 2018年12月12日

网站的前后端通信,往往会有异步请求,这时应该怎么设计 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,表示该链接永久性不再可用。

(完)

留言(21条)

轮询么。在http2时代应该不需要这种设计了吧

引用wbpmrck的发言:

轮询么。在http2时代应该不需要这种设计了吧

当客户端请求服务器资源是否创建完成时:如果服务器返回正在创建中,那客户端还是得轮询,时间至少是服务器返回的剩余时间(这个剩余时间不一定准确).

其实异步设计就是把一次来回通信中的处理等待时间与资源消耗,切成若干片段了。

API 设计上没看出有什么不同?好像就是返回的状态码不一样啊!

- 同步响应:201 Created
- 异步响应:202 Accepted

如果提前知道新创建的资源的路径 是不是可以直接用这个路径当query location? 这样就不需要处理query 过期的问题了

引用wbpmrck的发言:

轮询么。在http2时代应该不需要这种设计了吧

关键是理解并遵循这种规范,你想想你的API可读性可有这么清晰?没有遇到自己或合作方乱用Code,不明所以的时候?

阮大,你这个是非阻塞同步,而不算是异步.
按照阮大这个例子,异步是指在第一步,也就是发出那个post请求的时候,请求体就提供了回调URL.
同步: 就绪通知
异步: 完成通知

与其说异步api设计,不如说是对http status code的理解,有了理解,api设计自然就形成了,挺好,值得借鉴!

引用blizzard的发言:

阮大,你这个是非阻塞同步,而不算是异步.
按照阮大这个例子,异步是指在第一步,也就是发出那个post请求的时候,请求体就提供了回调URL.
同步: 就绪通知
异步: 完成通知

按我的理解他这个算是非阻塞异步...为什么是非阻塞同步?求指点

引用me7o的发言:

按我的理解他这个算是非阻塞异步...为什么是非阻塞同步?求指点

因为client端还在同步轮询?

现代浏览器怎么处理这类异步API?

简洁明了

做CI的话这种API应该不错

好文 赞!

使用多种status有什么好处吗?
很多客户端或者使用api的人会只认为200才是正确状态码。
如果仅仅是客户端跟api服务器直连还容易协商,如果中间使用了一些api manager之类的,
觉得会有麻烦。

我这边在设计时,会让客户端在异步请求开始的时候就定义最后的资源链接是否为消费一次即删除的链接

现代浏览器怎么处理这类异步API?

Jenkins的REST API用的是异步API吗?当你post一个的时候会先返回一个queue id,client之后可以通过这个queue id来找实际的build。坑爹的是queue id会在数分钟后失效。

清晰明了~赞!

这个和 http2.0 有什么关系。。。
这个重点在讲 API 设计,面向的客户端并不局限于浏览器。
感觉这样才是真正的 API 设计,http status code 应该是每个需要基于 http 协议构建客户端和服务的基石,
REST API 设计本来就需要遵循这个。

我要发表看法

«-必填

«-必填,不公开

«-我信任你,不会填写广告链接