选择一个 HTTP 状态码不再是一件难事

有什么能比 HTTP 响应状态码更简单呢?页面渲染了吗?好极了,返回 200。页面不存在?那么是 404。想要跳转到另一个页面?302 或者可能是 301

我喜欢把 HTTP 状态码想象成无线电波传输的 10 码1。“呼叫,呼叫,我是 White Chocolate Thunder,发现 200 OK。”

—— Aaron Patterson (@tenderlove) 2015-10-7

生活是美好的……直到有人告诉你,你还没有做这个 REST2。然后你该失眠了,因为你需了解是否你的新资源返回符合 RFC 规范3Roy-Fielding 规定的状态码。这里只有 200吗?或者为什么没有 204 No Content?或许有 202 Accepted……或者有 201 Created

使事情复杂化的是,官方的 HTTP/1.1 指南 —— RFC —— 是写于 1997 年的 在那一年,人们还在使用 Netscape 浏览器以 33.6 KB 的调制解调器来上网。在现在用 HTTP/1.1 就有点像把孙子兵法运用于现代企业战略,兵法无疑是伟大的,但我根本不知道如何具体运用。

t0139bc8ea6d24957db

能不能有一种直观的决策树来让你快速确定你要用到的少数状态码,并将那些不相关的和废弃的状态码排除掉?

当然可以,现在就能给你一个。

从何说起

t01b0d1355d11d5aa93

它可能看起来很可笑,但是我曾经看到过太多人分不清这些,“这里应该用 503 Service Unavaliable 还是 404 Not Found?”如果你曾经在这上面犹豫,那么你完全弄混了不同的响应类型,你的做法完全是错的。你应该回头看看上面这张流程图。

在深入到具体规范的流程图之前,有一些注意事项:

  • 我建议你阅读 RFC 7231httpstatuses.com
  • 以下内容对开发网站和设计 RESSTish API 有用。
    • 状态码对具体的 web server 实现是完全透明的
    • 当然这里完全忽略代理服务器
  • 我将响应状态码粗略地归为三类:

QQ截图20160724001104

最后但同样重要的,免责声明:我不保证我写的完全正确,我只是读了一些 RFC 文档在 Racksurg 公司实际工作时,每天用它来实现有用的 API。如果你认为我是错的或者我轻视了你最喜欢的状态码,那可能是我的失误,你可以通过评论告知我究竟错在哪里。

2XX/3XX

t01fcb848858d6f4e79

4XX

t011cad6f61ee78675d

5XX

t01e7b981390190e781

结语:究竟为什么状态码重要

我并不完全确定它们真的重要。

Facebook 上有许多聪明人,他们创建了 API 只返回 200

反对挑选指定状态码的基本观点是:现有的状态码对于现代网站/API来说太通用了。它无法让客户端以任何一种有意义的方式处理包含特定应用格式的细节的返回信息 —— 例如表单的哪一个字段校验失败了以及为什么失败了。既然如此,那么为什么要在多余的没什么用的 HTTP 状态码上浪费时间?

如果要给出一个理由,来说明为什么使用特定的状态码很重要,公认的理由是 HTTP 是一个分层的系统,如果响应状态码是有特定含义的,任何代理、缓存或者位于客户端和服务器之间的 HTTP 框架能够更好地工作。我不认为这个理由足够更令人信服,尤其现在每个人都开始将服务迁移到 HTTPS,我们禁止了任何不被服务器直接控制的代理或缓存节点。

然而,我会给你三个理由为什么我认为状态码仍然很重要:

  1. 客户端已经处理(或者可以方便地被扩展以便处理)具有特定行为的不同状态码:
    • 相比于 302 Found301 Moved Permanently 在 Google 等搜索引擎上有更好的 SEO 效果。
    • 301 Moved Permanently 能够被缓存,而 429 Too Many Requests 不被缓存等等。 有的客户端库可以处理 428 Too Many Request,将请求回退并一天之后再次尝试请求。 有的客户端可以用同样的方式处理 503 Service Unavilable
  2. 即使对现代需求不能完全满足,许多状态码依然代表着值得处理的特定响应(因此为什么不直接使用标准状态码?)。
    • 那些本该返回 405 Method Not Allowed 却返回 404 的 API 让我疯狂地想,“我是否敲错了 URL 或者我请求用错了 HTTP method?”
    • 我能告诉你如果我们返回 502 Bad Gateway(上游服务问题)而不是返回让人困惑的 500 Internal Server Error,那么我们曾经能节省许多调试问题的时间。
  3. 不管你信不信,反正我是信了,在广泛被使用的 API 中正在建立一个约定,以返回状态码例如 201 Created429 Too Many Requests 以及 503 Service Unavialable。如果你遵循这个约定,用户将能更方便地使用你的网站/API并且解决任何他们可能遇到的问题。

在这里面,决定什么时候返回何种状态码是最难的,然而有了正确的知识(别再说我不知道,仔细看前面的流程图),挑选一个有意义的状态码变得简单很多。

说明

别去研究 RFC 2616,更别去研究 RFC 2068,真正有用的是 RFC 7231

参考资料

1 7 收藏 1 评论

相关文章

可能感兴趣的话题



直接登录
最新评论
  • 白一梓 程序员 2016/08/13

    说白了,作者啰嗦了这么多依然没有梳理清楚HTTP返回码的使用场景。是在网站型的web程序中使用,还是在API型的程序中使用。假设是在网站型的程序中使用,网站说白了是给用户看的,诚然网页不存在要返回404,但是这个功能web服务器一般会帮我们做,不过单纯的在出错的时候使用一个服务器给出的默认页面是不够的,网站是给人看的,用户才不管你返回码设置的有多科学,你就是把返回码玩出花来,他们也不买你的帐。所以说,对于网站来说,可以给出常用的返回码,但是重点在于展示的页面。至于是不是返回码是400 401之类的,你做再讲究不如一个出错信息提示页来的简洁明快。

    对于API型的应用来说,假设这个API是给前端ajax用的,如果你在业务逻辑出错的时候给出了一个自定义的错误码,那么它只会在控制台上打印一条错误日志。如果你用的是jquery来处理的ajax,那么它就会触发error函数回调,这肯定不是你想要的,正常的做法是返回json数据,这个json中带有返回码和错误信息,在出错的时候将json中的错误信息字段显示出来。如果你在出错时用返回码,则在error回调中还拿不到具体的错误原因,你只能显示一个模糊的信息,操作xxx出错了,这肯定不是用户想要的,这同时增加了客服的难度,用户再跟客服描述的时候只能描述一个模糊的问题。当然还有一个隐藏的问题,假设你使用了反向代理,前端程序也没法区分当前的非法的返回码到底是web应用程序返回的,还是反向代理服务器返回的。

    假设你在纯服务器之间的API之间使用自定义HTTP返回码,你会发现开发者仅仅解析一个HTTP返回码是不够的,他们想知道错误的详细信息,这时候你还得返回一个json数据。当然上面说的反向代理的问题,在这里依然存在。

    所以综上所述,我是不推荐使用自定义HTTP返回码的,虽然它很科学,但是它太学究,不实用。特别对于网站应用程序遇到链接找不到或者未捕获异常,才返回404或者500,而且返回的时候要使用友好的出错页来承载,其他逻辑错误也尽量使用错误页来承载。

     

跳到底部
返回顶部