我正在尝试用我正在开发的“类rest”API找出在不同场景中返回的正确状态代码。假设我有一个端点,允许以JSON格式POST'ing购买。它是这样的:

{
    "account_number": 45645511,
    "upc": "00490000486",
    "price": 1.00,
    "tax": 0.08
}

如果客户端发送给我“sales_tax”(而不是预期的“tax”),我应该返回什么?目前,我要返回400分。但是,我开始质疑自己。我真的应该退回422吗?我的意思是,它是JSON(这是受支持的),它是有效的JSON,它只是不包含所有必需的字段。


当前回答

400 -请求验证失败,比如数据丢失,类型错误等等,所以它的状态是400。

422 -通过请求验证,但操作过程失败,因为请求数据,或它的一部分给操作一个错误,但被处理,并给出一个状态422。

其他回答

400 Bad Request是用例的HTTP状态代码。该代码由HTTP/0.9-1.1 RFC定义。

由于格式错误,服务器无法理解请求 语法。客户端不应该重复请求 修改。

https://www.rfc-editor.org/rfc/rfc2616#section-10.4.1

422不可处理实体由RFC 4918 - WebDav定义。请注意,与400相比有轻微的差异,见下面引用的文本。

如果XML 请求主体包含格式良好的(即语法正确),但是 语义错误的XML指令。

为了保持统一的接口,你应该只在XML响应的情况下使用422,你还应该支持Webdav扩展定义的所有状态码,而不仅仅是422。

https://www.rfc-editor.org/rfc/rfc4918#page-78

另见Mark Nottingham关于状态代码的帖子:

试图“深入”映射应用程序的每个部分是错误的。 转换成HTTP状态码;在大多数情况下,粒度级别你 想要的目标要粗糙得多。有疑问时,可以使用 通用状态代码200 OK, 400 Bad Request和500 Internal 没有更好的匹配时的服务错误。

如何看待HTTP状态码

400 Bad Request现在似乎是用例中最好的HTTP/1.1状态代码。

在你提出问题(以及我最初的回答)的时候,RFC 7231还不存在;在这一点上,我反对400坏请求,因为RFC 2616说(强调我的):

由于语法错误,服务器无法理解请求。

并且您描述的请求是语法上有效的JSON,封装在语法上有效的HTTP中,因此服务器对请求的语法没有任何问题。

然而,正如Lee Saferite在评论中指出的那样,RFC 7231取代了RFC 2616,不包括这个限制:

400(坏请求)状态码表示服务器不能或不愿处理请求,原因是客户机错误(例如,格式错误的请求语法、无效的请求消息框架或欺骗性的请求路由)。


然而,在重新措辞之前(或者如果你想争论RFC 7231现在只是一个提议的标准),422不可处理实体对于你的用例来说似乎不是一个不正确的HTTP状态代码,因为RFC 4918的介绍说:

而HTTP/1.1提供的状态码足以 描述WebDAV方法遇到的大多数错误条件 是一些不能完全归入现有类别的错误。 该规范定义了为WebDAV开发的额外状态代码 方法(第11节)

422页的描述说

422(不可处理实体)状态码表示服务器 理解请求实体的内容类型(因此是 415(不支持的媒体类型)状态码不合适) 请求实体的语法是正确的(因此是400(坏请求) 状态码不合适),但无法处理包含的 指令。

(注意对语法的引用;我怀疑7231也部分淘汰了4918)

这听起来和你的情况一模一样,但以防你有任何疑问,它接着说:

例如,如果XML 请求主体包含格式良好的(即语法正确),但是 语义错误的XML指令。

(将“XML”替换为“JSON”,我想我们可以同意这是你的情况)

现在,有些人会反对RFC 4918是关于“Web分布式编写和版本控制(WebDAV)的HTTP扩展”,你(大概)没有做任何涉及WebDAV的事情,所以不应该使用它。

如果要在使用原始标准中明确不包含该情况的错误代码和使用来自扩展的准确描述该情况的错误代码之间做出选择,我会选择后者。

此外,RFC 4918第21.4节引用了IANA超文本传输协议(HTTP)状态码注册表,其中可以找到422。

我建议HTTP客户端或服务器使用来自该注册中心的任何状态代码是完全合理的,只要它们正确地这样做。


但是对于HTTP/1.1, RFC 7231有吸引力,所以只使用400个坏请求!

没有正确的答案,因为这取决于对请求的“语法”的定义。最重要的是你:

一致地使用响应代码 在响应体中包含尽可能多的额外信息,以帮助使用API的开发人员弄清楚发生了什么

在大家因为我说这个问题没有正确或错误的答案而责怪我之前,让我解释一下我是如何得出这个结论的。

在这个特定的例子中,OP的问题是关于一个包含不同于预期键的JSON请求。现在,从自然语言的角度来看,接收到的键名与预期的键名非常相似,但严格来说,它是不同的,因此(通常)机器不会认为它们是等效的。

如上所述,决定因素是语法的含义。如果请求是以application/json的内容类型发送的,那么是的,请求在语法上是有效的,因为它是有效的json语法,但在语义上是无效的,因为它不匹配预期的内容。(假设有一个严格的定义,什么使问题中的请求在语义上有效或无效)。

另一方面,如果请求是以更特定的自定义内容类型(如application/vnd.mycorp)发送的。mydatatype+json,也许,确切地指定了期望的字段,那么我会说请求很容易在语法上无效,因此响应为400。

在这个问题中,由于键是错误的,而不是值,因此如果存在有效键是什么的规范,就会出现语法错误。如果没有有效键的规范,或者错误是带有值的,那么它将是语义错误。

为反映截至2015年的状况:

从行为上讲,客户端和中介端对400和422响应代码的处理是一样的,因此使用哪一种响应代码并没有什么具体的区别。

然而,我希望看到400目前被更广泛地使用,而且HTTPbis规范提供的说明使它成为两种状态代码中更合适的一个:

HTTPbis规范明确了400的意图,不只是用于语法错误。现在使用了更广泛的短语“表示服务器不能或不愿处理由于客户机错误造成的请求”。 422是一个特定的WebDAV扩展,在RFC 2616或更新的HTTPbis规范中没有引用。

就上下文而言,HTTPbis是HTTP/1.1规范的修订版,试图澄清不清楚或不一致的地方。一旦达到批准状态,它将取代RFC2616。

您的案例:从REST的角度来看,HTTP 400是您案例的正确状态代码,因为发送sales_tax而不是tax在语法上是不正确的,尽管它是一个有效的JSON。在将JSON映射到对象时,大多数服务器端框架通常会强制执行这一点。然而,有一些REST实现忽略JSON对象中的new键。在这种情况下,服务器端可以强制使用只接受有效字段的自定义内容类型规范。

422的理想场景:

在理想情况下,如果服务器理解请求实体的内容类型,并且请求实体的语法正确,但由于语义错误而无法处理数据,则首选422,并且通常可以接受将其作为响应发送。

400 / 422的情况:

记住,响应代码422是一个扩展的HTTP (WebDAV)状态码。仍然有一些HTTP客户端/前端库还没有准备好处理422。对他们来说,这就像“HTTP 422是错误的,因为它不是HTTP”一样简单。从服务的角度来看,400并不十分具体。

In enterprise architecture, the services are deployed mostly on service layers like SOA, IDM, etc. They typically serve multiple clients ranging from a very old native client to a latest HTTP clients. If one of the clients doesn't handle HTTP 422, the options are that asking the client to upgrade or change your response code to HTTP 400 for everyone. In my experience, this is very rare these days but still a possibility. So, a careful study of your architecture is always required before deciding on the HTTP response codes.

为了处理类似的情况,服务层通常使用版本控制或设置配置标志对严格符合HTTP的客户端发送400,对其余客户端发送422。通过这种方式,它们为现有的使用者提供了向后兼容性支持,但同时也为新客户提供了使用HTTP 422的能力。


RFC7321的最新更新说:

The 400 (Bad Request) status code indicates that the server cannot or
   will not process the request due to something that is perceived to be
   a client error (e.g., malformed request syntax, invalid request
   message framing, or deceptive request routing).

这证实了服务器可以为无效请求发送HTTP 400。400不再仅仅是语法错误,但是,422仍然是一个真正的响应,前提是客户端可以处理它。