HTTP状态代码,用于“静态处理”


47

我正在构建一个RESTful API,该API支持对长时间运行的任务进行排队以进行最终处理。

该API的典型工作流程为:

  1. 用户填写表格
  2. 客户端将数据发布到API
  3. API返回202已接受
  4. 客户将用户重定向到该请求的唯一URL(/results/{request_id}
  5. 〜最终〜
  6. 客户再次访问URL,并在该页面上看到结果。

我的麻烦在于步骤6。每当用户访问该页面时,我都会向我的API(GET /api/results/{request_id})发出请求。理想情况下,该任务将在此时完成,并且我将与他们的任务结果一起返回200 OK。

但是用户很吃力,我希望在结果尚未完成处理时会进行许多过度的刷新。

我最好的状态代码指示是什么:

  • 该请求存在,
  • 还没有完成
  • 但它也没有失败。

我不希望有一个代码可以传达所有这些信息,但是我希望可以让我传递元数据而不是让客户期望内容。

返回202可能是有意义的,因为在这里没有别的意思:这是一个GET请求,因此可能没有任何东西被“接受”。那是一个合理的选择吗?

所有这些的明显替代方案-起作用,但违反了状态码的一个目的-将始终包括元数据:

200 OK

{
    status: "complete",
    data: {
        foo: "123"
    }
}

...要么...

200 OK

{
    status: "pending"
}

然后客户端,我会(叹息)switchresponse.data.status,以确定该请求是否已完成。

这是我应该做的吗?还是有更好的选择?Web 1.0对我而言是如此。


1
1xx代码不是专门用于此目的吗?
安迪

@Andy我正在看102,但这是WebDAV的东西。除此之外,不...它们主要用于运输途中的通信。在切换到Web套接字等时很有用。
马修·豪根

你在说什么延迟?10秒?还是6个小时?如果延迟很短并且通常在同一浏览器访问中,则您可能会执行长时间轮询或Web套接字,而不是定期轮询。
GrandmasterB

@GrandmasterB可能需要几个小时。我不负责工作处理本身,因此我没有一个很好的估计,但这将需要一段时间。否则,我将第一个POST请求保持打开状态。长轮询或Web套接字的主要问题是用户可能会关闭浏览器并返回。那时我可以再次打开它们(这就是我的工作),但是在打开这些套接字之前可以调用一个API似乎更干净,因为出现问题的边缘情况很明显。
马修·豪根

Answers:


48

接受HTTP 202(HTTP / 1.1)

您正在寻找HTTP 202 Accepted状态。参见RFC 2616

该请求已被接受进行处理,但是处理尚未完成。

HTTP 102处理(WebDAV)

RFC 2518建议使用HTTP 102 Processing

102(正在处理)状态码是一个临时响应,用于通知客户端服务器已经接受了完整的请求,但尚未完成。

但有一个警告:

请求完成后,服务器必须发送最终响应。

我不确定如何解释最后一句话。服务器是否应该避免在处理过程中发送任何内容,而仅完成做出响应?还是仅在处理终止时才强制结束响应?如果您想报告进度,这可能很有用。发送HTTP 102并逐字节(或逐行)刷新响应。

例如,对于一个漫长而线性的过程,您可以发送一百个点,每个字符后都冲洗一次。如果客户端(例如JavaScript应用程序)知道应该精确到100个字符,则可以将其与进度条匹配以显示给用户。

另一个示例涉及由几个非线性步骤组成的过程。在每个步骤之后,您都可以刷新一条日志消息,该消息最终将显示给用户,以便最终用户可以知道该过程的进展情况。

逐步冲洗的问题

请注意,尽管这项技术有其优点,但我不推荐使用。原因之一是它迫使连接保持打开状态,这可能会损害服务的可用性,并且无法很好地扩展。

更好的方法是使用进行响应,HTTP 202 Accepted或者让用户稍后与您联系以确定处理是否结束(例如,通过反复调用给定URI,例如/process/result将以HTTP 404 Not Found或HTTP 409 Conflict响应的方式进行处理,直到该过程为止)。完成并准备好结果),或者如果您能够通过消息队列服务(例如)或WebSockets 回叫客户端,则在处理完成时通知用户。

实际例子

想象一下一个转换视频的Web服务。入口点是:

POST /video/convert

它从HTTP请求中获取视频文件并对其进行处理。让我们想象一下魔术是CPU密集型的,因此它不能在请求传输期间实时完成。这意味着一旦文件传输完毕,服务器将以HTTP 202 AcceptedJSON内容响应,表示“是,我收到了您的视频,并且正在处理;它将在将来的某个地方准备就绪,并且可以通过ID 123获得。”

客户端可以订阅消息队列,以便在处理完成时得到通知。完成后,客户端可以通过以下方式下载处理后的视频:

GET /video/download/123

这导致一个HTTP 200

如果客户端在收到通知之前查询此URI,会发生什么情况?好吧,服务器将响应,HTTP 404因为实际上视频还不存在。它可能当前正在准备中。可能从来没有要求过。它可能在过去的某个时间存在,并在以后删除。重要的是生成的视频不可用。

现在,如果客户不仅关心最终的视频,而且还关心进度(如果没有消息队列服务或任何类似的机制,这将显得尤为重要)怎么办?

在这种情况下,可以使用另一个端点:

GET /video/status/123

这将导致类似于以下的响应:

HTTP 200
{
    "id": 123,
    "status": "queued",
    "priority": 2,
    "progress-percent": 0,
    "submitted-utc-time": "2016-04-19T13:59:22"
}

一遍又一遍地执行请求将显示进度,直到达到:

HTTP 200
{
    "id": 123,
    "status": "done",
    "progress-percent": 100,
    "submitted-utc-time": "2016-04-19T13:59:22"
}

区分这三种类型的请求至关重要:

  • POST /video/convert将任务排队。它应该仅被调用一次:再次调用将使其他任务排队。
  • GET /video/download/123涉及的结果操作的:在资源是视频。在这里,处理(即在请求之前独立于请求准备实际结果的幕后处理)无关紧要。可以调用一次或多次。
  • GET /video/status/123关于处理本身。它没有排队。它不在乎最终的视频。该资源是处理本身。可以调用一次或多次。

1
202响应有意义GET吗?那当然是对于initial的正确选择POST,这就是为什么我要使用它。但是,GET当a不接受来自该特定请求的任何内容时,说“已接受”在语义上是可疑的。
马修·豪根

2
@MainMa就像我说的那样,我先将POST要排队的工作上去,然后再看GET结果,可能是在客户关闭会话之后。404是我考虑过为好,但它似乎是错误的,因为该请求发现,它只是尚未完成。这将向我表明未找到排队的工作,这是非常不同的情况。
马修·豪根

1
@MatthewHaugen:做GET零件时,不要将其视为不完整的请求,而应将其视为获取操作结果的请求。例如,如果我告诉您转换视频,并且花了您五分钟的时间来处理,那么两分钟后请求转换后的视频导致HTTP 404,因为该视频还不存在。请求对所述操作本身的进度,在另一方面,可能会导致含有的字节转换,数,速度等的HTTP 200
Arseni Mourzenko

5
对于资源尚不可用的HTTP状态代码,建议返回409冲突响应(“由于与资源的当前状态冲突,请求无法完成。”),而不是404响应(如果资源没有响应)之所以不存在,是因为它处于生成的中间。
布赖恩

1
@Brian您的意见将对这个问题做出合理的回答。尽管我随后会回答“仅在预期用户可能能够解决冲突并重新提交请求的情况下才允许使用他的代码”,但这在我的情况中并不完全正确,但这似乎比“未找到”少错了。我的一部分倾向钉在Retry-After头上的409。唯一的问题是,为GET返回409似乎很奇怪,但是我可以忍受这种怪异-将来不太可能定义它。
马修·豪根

5

所有这些的明显替代方案-起作用,但违反了状态码的一个目的-将始终包括元数据:

这是正确的方法。资源相对于特定于域的日志(也称为业务逻辑)所处的状态与资源表示的内容类型有关。

这里有两个不同的概念,它们实际上是不同的。一个是资源的客户端和服务器之间的状态转移的状态,另一个是在业务域支持该资源的不同状态的上下文中资源本身的状态。后者与HTTP状态代码无关。

请记住,HTTP状态代码与正在处理的资源的客户端和服务器之间的状态转移相对应,而与该资源的任何细节无关。当您GET使用资源时,客户端会要求服务器提供资源处于其当前状态的表示形式。这可能是小鸟的照片,可能是Word文档,也可能是当前外部临时文件。HTTP协议无关紧要。HTTP状态代码对应于该请求的结果。难道POST从客户机到服务器的资源转移到服务器,然后服务器给它的客户端可以查看网址?是?那就是201 Created回应。

该资源可能是当前处于“待审核”状态的航空公司预订。或者可能是处于“已批准”状态的产品采购订单。这些状态是特定于域的,而不是HTTP协议的含义。HTTP协议处理客户端和服务器之间的资源传输。

REST和HTTP的要点是协议并不关心资源的细节。这是有目的的,它与特定于域的问题无关,因此可以在无需了解特定于域的问题的情况下使用它。您无需重新解释HTTP状态代码在每个不同上下文中的含义(机票预订系统,想象处理系统,视频安全系统等)。

特定于域的内容是供客户端和服务器根据Content Type资源的资源在它们之间进行计算的。HTTP协议与此无关。

至于客户端如何判断“请求”资源已更改状态,轮询是最好的选择,因为它可以控制客户端并保持不间断的连接。尤其是在可能需要数小时才能更改状态之前。即使您对REST感到不满,您也只是要保持连接打开,将其保持打开状态数小时,并假设没有任何问题会是一个坏主意。如果用户关闭客户端或网络断开该怎么办。如果粒度为小时,则客户端可以每隔几分钟请求状态,直到请求从“待处理”更改为“完成”。

希望可以帮助澄清事情


“从客户端到服务器的POST是否已将资源传输到服务器,然后服务器在服务器上为其提供了可供客户端查看的URL?是吗?这就是201创建的响应。” 202如果服务器不能立即采取行动来处理资源(OP正在这样做),则“接受”也可以作为对此的响应。
安迪

1
问题是服务器正在立即行动。它立即使用URL创建资源。只是资源的状态为“待处理”(或其他状态)。那是一个业务领域状态。就HTTP协议而言,服务器在创建资源并向客户端提供资源URL时便立即采取行动。您可以获取该资源。POST请求本身未挂起。我的意思是将两个不同的概念域分开。如果客户端正在发送通知并且忘记了几个小时未执行的POST请求,则适用202。
Cormac Mulhall

没有人关心URL是否存在,但是由于资源仍在处理中,因此您无法获取该资源表示的数据。最好不要使用该URL,直到可以将其用于获取视频为止。
安迪

资源已创建,它只是处于“待处理”状态。这本身就是相关数据。将来的某个时候,服务器可能会将资源状态更改为“完成”(或“失败”),但这与HTTP域特定任务“创建资源”的概念不同。挂起对于“请求”资源而言可能是一个完全有效的状态,并且客户端显然希望知道服务器已在该状态下创建了该资源,因为它从要求服务器创建资源开始以轮询方式来查找资源。如果状态改变了。
Cormac Mulhall

4

我发现此博客中的建议合理:REST和长期运行的作业

总结一下:

  1. 服务器返回代码“ 202 Accepted”,其中“ Location”标头设置为URI,以便客户端检查状态,例如“ / queue / 12345”。
  2. 在处理完成之前,服务器以“ 200 OK”和一些显示作业状态的响应数据来响应状态查询。
  3. 处理完成后,服务器使用包含最终结果的URI的“ 303 See Other”和“ Location”响应状态查询。

2

如果资源不存在(因为正在生成),则该资源尚不可用的HTTP状态代码建议返回409冲突响应,而不是404响应。

w3规范

10.4.10 409冲突

由于与资源的当前状态存在冲突,因此无法完成请求。仅在预期用户可能能够解决冲突并重新提交请求的情况下才允许使用此代码。响应正文应包含足够的内容

供用户识别冲突源的信息。理想情况下,响应实体应包括足够的信息供用户或用户代理解决问题。但是,这可能是不可能的,也不是必需的。

响应PUT请求最有可能发生冲突。例如,如果正在使用版本控制,并且正在PUT的实体包括对资源的更改,该更改与先前的(第三方)请求所进行的更改冲突,则服务器可能会使用409响应来指示它无法完成请求。在这种情况下,响应实体可能会以响应Content-Type定义的格式包含两个版本之间差异的列表。

这有点尴尬,因为409代码“仅在预期用户可能能够解决冲突并重新提交请求的情况下才允许使用”。我建议响应主体包含一条消息(可能以某种响应格式与您的API的其余部分相匹配),例如,“此资源当前正在生成。它已在[TIME]启动,预计在[TIME]完成。请稍后再试。”

请注意,如果请求资源的用户也很可能是发起生成该资源的用户,那么我只会建议使用409方法。不参与资源生成的用户将发现404错误,从而减少混乱。


看起来像409真正意味着什么,这是对推杆的反应。
安迪

@安迪:是的,但其他选择也是如此。例如,202实际上是对启动处理的请求的响应,而不是请求处理结果的请求。实际上,最符合规范的响应是404,因为未找到资源(因为它尚不存在)。没有什么可以阻止API在404响应中提供相关的api数据。请注意,4xx / 5xx的响应往往很烦人。有些语言会引发异常,而不仅仅是提供不同的状态代码。
布莱恩

2
不,尤其是MainMa的回答的最后几段。单独的端点可以检查请求的状态并获取视频本身。状态与视频资源不同,应单独寻址。
安迪
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.