Learn HTTP Status Code Part.1

by xujiwei(http://www.xujiwei.com/)

1. 前言

HTTP 状态码在 Web 开发中是相当重要的一部分,掌握了 HTTP 的运用,可以在网站速度、性能等等的优化上有迹可循。当前,掌握 HTTP 状态码也可以为进一步加深对 HTTP 协议的理解打下基础。

以前,我所熟知的 HTTP 状态码仅限于 200、404、302 等几个,但是在一次与 smallshell 聊天的过程中,谈到了 HTTP 状态码,让我对剩余许多我并不熟知的 HTTP 状态码起了兴趣。不过在这之后的一段时间里都比较忙,因此这个想写的这个系列就一直拖着,这几天算是稍微清闲下来了,准备好好学习了一下 HTTP 状态码。

嗯,我也是在学习的过程中,所以,如果我写的有什么不对,欢迎指正,留言或 email(vipxjw at 163 dot com) 均可:)

2. 准备

本文基于 RFC2616 第10节 Status Code Definitions 而写,如果对本文有不明白的地方,可以参考完整的 HTTP 1.1 协议

3. 1xx 系列

1xx 系列一般是用在服务器与代理之间,表示一些信息,这在我们写服务端程序时并不常用,并且 1xx 系列状态码是在 HTTP 1.1 中才加入的,按照 w3c 的说法,除非在实验性的环境下,不要发送 1xx 系列状态码到使用 HTTP 1.0 协议的客户端

如果需要了解两个 1xx 系列的状态 100 Continue 和 101 Switching Protocols 的作用,可以在 RFC2616 第10节中找到相关资料。

4. 2xx 系列

2xx 系列表示成功的响应,最为人所知的就是 200 OK 了,它表示一下正确响应了客户端所发送过来的请求,那么在客户端,也就需要以“正确”的态度来对待服务端过来的响应内容。

4.1 200 OK

呃,200 OK 是最最常见的状态码了,每次我们在浏览器输入一个网址按下回车后,就会收到一堆 200 OK,就是一堆 200 OK,呈现给我们了一个丰富多彩的页面。

因此,通常情况下,在编写服务端程序时,也应该返回一个 200 OK 的状态码。但是也因为 200 OK 是如此的普通,如此的常用,可能在一些特殊的情况下,开发人员没有注意到 200 OK 并不是一个可取的状态码,而这个特殊的情况,最明显的一个就是采用了 自定义错误页 或 Url Rewrite 的时候。

在使用了自定义错误页或者 Url Rewrite 时,如果碰到了一个并不存在的资源,服务器会将请求重定向到指定的页面,而这时服务器会认为请求得到了正确的响应,响应状态码会被设置为 200 OK,如果在程序中不另加设置,客户端就会请求一个不存在的资源时得到了 200 OK 这个本该只在得到正确响应时才能得到的状态码。这对于一般用户来说,可能没有多大的影响,但对于搜索引擎却是不友好的,搜索引擎会不断的索引这些并不存在的资源。

所以,如果使用了自定义错误页或 Url Rewrite,那么需要在程序中根据实际情况来设置状态码是 200 OK 还是 404 Not Found

例如,在 IIS 中设置自定义错误页为 404.asp 后,IIS 会把对不存在资源的请求重定向到 404.asp,状态码被设置为 200 OK,在 404.asp 中,就需要把状态码给改回来:

  • <%
  • Response.Status = "404 Not Found"
  • %>

4.2 201 Created

201 Created 通常用来表示请求的资源不存在,但是已经被创建了。一般可以用于一些发布协议,如 Atom Publishing Protocol 等,你在使用 Atom 协议去发布一篇 blog 时,服务端可能会返回一个 201 Created 的状态码,表示你发送的请求已经被接受了,并且你的文章也已经成功创建了。

如果服务端要返回一个 201 Created 状态码,那么同时也应该在响应体头部的 Location 字段中指出新创建资源的位置,例如如果是发布一篇 blog,那么服务端在返回 201 状态码的时候,同时在 Location 字段中指出这篇新的 blog 的 URI 是什么,这样客户端可以根据这个 URI 立即去访问这篇 blog。

4.3 202 Accepted

在 201 Created 中提到,使用发布协议时,服务端可以返回一个 201 的状态码,提示客户所发送的请求已经被正确响应,并且资源也已经被正确创建了。但是在某些时候,客户所请求的资源并不是可以立即创建,可能需要被审核,可能在一个队列当中等等,在这时,服务端并不能直接返回一个 201 Created 状态码,因为返回这个状态码时,客户端认为它所发送的请求已经被正确处理了,但事实中,这个请求仍旧在处理过程之中。

这个时候,202 Accepted 状态码就是一个更好的选择。202 Accepted 可以理解为:你的请求我收到了,但是还没有处理完成,请稍候。更实际一点,你可以理解为你去某个 Google Group 申请了成员资格,Google Group 系统接受了你的申请,但是这个 Group 是需要管理员审核的,于是,你得到了一个 202 Accepted 的状态,表示你的请求已经被收到了,但是还没有处理完成,你需要等待。

202 Accepted 要求客户端等待服务端的处理完成,但是 HTTP 协议是无状态的,因此服务端并不会也不能主动去通知客户商,说“哎,你的请求已经处理好了,地址是xxx”。那么,跟踪请求状态这个任务就需要客户端去做。但是客户端要跟踪,也得有个对象,那个这个对象就是服务端该提供给客户端的了。

服务端在返回 202 Accepted 状态码的同时,应该给出当前请求的状态,或者给出一个状态监视器的地址。

服务端直接在响应 202 Accepted 状态码的同时给出请求处理的状态就相当于我们打电话去订餐,第一次打电话给餐厅,餐厅响应一个 202 Accepted:“啊,我们收到你的订单了,一会儿就给您送去。”,那么等了一会,怎么还没来,再打电话过去,餐厅接着响应一个 202,并告诉我们:“您的订单已经在路上了,过会就能收到了,请稍等。”,于是,我们可以通过不断地发送请求来知道我们的订单到底怎么样了。

而另外一种方式,服务端返回一个状态监视器的地址则相当在我们在申请某个 Google Group 成员资格时,提交申请之后,我们要做的就是看看邮箱,看看有没有来自 Google Group 的审核邮件。

当然,客户端与服务端对 202 Accepted 所返回的响应体应该有一个共同的规范,这样才能通过这个响应来了解请求的处理进程。

4.4 203 Non-Authoritative Information

203 表示非权威应答,说明这个响应头可能并不是原始服务器的响应头,在中间传输过程中附加了一些其他的信息,诸如通过了一些代理,加了一些额外的头部信息等等。

4.5 204 No Content

服务端返回 204 No Content 这个状态码时,表示客户端发送的已经正确响应了,但是并不会返回响应实体,只会更新头部信息。客户端在收到 204 No Content 时,当前显示的内容不会变化,显示的内容还是之前的文档,只是地址栏里的地址已经变成新的网址。

4.6 205 Reset Content

按照 RFC2616 中的描述,客户端在收到 205 Reset Content 状态码时,应该重置当前文档内容,相当于重新加载发送请求的页面,但是测试发现,Firefox 3.0 与 Chrome 依旧显示当前文档,不会重新加载,而 Safari 和 IE 则直接重定向到了所请求的网址,相当于不支持 205 Reset Content 状态码。

4.7 206 Partial Content

206 Partial Cotent 在多线程下载中相当常见,它表示服务端只处理了部分内容或者是只返回了部分内容。

按照 RFC2616 说明,在返回 206 Partial Content 状态码时,服务端需要在头部中指定一些必需的字段,如 Date、Content-Length 等,这些头部字段的使用都有一定的条件及限制,如 Content-Length 必须与响应体的实际大小一致,如果在响应 200 OK 时服务端发送了 ETag 或者 Content-Location 的头部字段,在响应 206 Partial Content 时也必须发送同样的头部字段,具体可以参考 RFC2616 Section 10.2.7

例如可以在 ASP 中实现部分响应来支持多线程下载:

  • <%@LANGUAGE="JScript" CODEPAGE="65001"%>
  • <%
  • // code from www.xujiwei.com
  • // 获取请求中是否有 Range 参数
  • var http_range = Request.ServerVariables("HTTP_RANGE") + ';
  • var filename = "asplite.zip";
  • // 创建 ADODB.Stream 对象并载入文件
  • var stream = new ActiveXObject("ADODB.Stream");
  • stream.Type = 1;
  • stream.Mode = 3;
  • stream.Open();
  • stream.LoadFromFile(Server.MapPath(filename));
  • // 让客户端自动识别文件类型及文件名
  • Response.ContentType = "application/x-zip-compressed";
  • Response.AddHeader("Content-Disposition", "inline; filename=" + filename);
  • // 判断是否响应部分
  • if(http_range == ' || http_range == 'undefined') {
  • Response.Status = "200 OK";
  • Response.BinaryWrite(stream.Read());
  • }
  • else {
  • Response.Status = "206 Partial Content";
  • var ranges = http_range.substr(6).split("-");
  • var start = +ranges[0], end = +ranges[2];
  • stream.Position = start;
  • var size = end - start;
  • if(size > 0) {
  • Response.BinaryWrite(stream.Read(size));
  • }
  • else {
  • Response.BinaryWrite(stream.Read());
  • }
  • }
  • %>

当然,上面代码中的判断部分并不严密,但是,多线程下载工具也并不一定严格遵守这些规则,所以,还是可以偷偷懒的。

后记

断网了几天,断电了两天,这篇文章终于还是出来了。1xx 和 2xx 系列的写完了,接下来就是重定向的 3xx 系列了。

参考资料

1. HTTP Status: 201 Created vs. 202 Accepted

2. Google 帮助:HTTP 状态代码

3. 兔子洞:HTTP响应状态码