📘 第六章 HTTP首部
📄 一句话概括

Allow 列出这个资源支持哪些方法。 它最常出现在 405 和 OPTIONS 的响应里,作用是让客户端知道「换个方法再试」。

📝 本节和前面几节的关系

响应首部的九个讲完了,从这一节开始是实体首部。 第一个却是关于「方法」的——它描述的不是正文,而是这个地址能接受什么动作。

一、它回答的是「你能怎么动我」

一个资源支持 GET 和 HEAD,不一定支持 DELETE。Allow: GET, HEAD, OPTIONS 就是把这句话说清楚。 值是一串方法名,逗号分隔,全是大写。 OPTIONS 是专门用来问这件事的:客户端发 OPTIONS /doc,服务端回 204 加一行 Allow, 正文为空。这是标准的探测手段,跨域预检请求(CORS preflight)就是它的变种。 注意它和 Access-Control-Allow-Methods 不是一回事:后者是 CORS 专用的,前者是 HTTP 本身的。 两者经常一起出现在预检响应里。

二、405 里必须带

用了一个不支持的方法,服务端回 405 Method Not Allowed,并且必须在响应里带 Allow。 这条是规范里的硬要求。 Demo 里跑了对照:不带 Allow 的 405 是 133 字节,带的是 160 字节,差 27 字节。 这 27 字节买的不是「礼貌」,是可恢复性。没有它,客户端只知道「我错了」,不知道该改成哪个方法; 有了它,客户端能自己换一条路重试,不用人来猜。

三、三个常见误解

第一,Allow 不是「服务器支持的全部方法」,而是「这一个资源支持的方法」。 同一台服务器上,/a 可能只允许 GET,/b 允许 GET 和 PUT。 第二,Allow 不保证方法一定成功。 它说的是「这个动作在语义上被接受」,能不能做成还要看权限、看状态——那是 403 和 409 的事。 第三,HEAD 要不要列?如果支持 GET,按规范就该支持 HEAD。很多框架自动带上,自己实现的时候别忘了。

四、动手 Demo:OPTIONS 问一次,DELETE 撞一次

服务端只认 GET、HEAD、OPTIONS 三个方法。 先用 OPTIONS 问它都会什么,再用 DELETE 去撞,分别看带 Allow 和不带 Allow 的 405 差在哪。

SUPPORT = (u'GET', u'HEAD', u'OPTIONS')

def fn(raw, st):
    meth = raw.split(b' ')[0].decode('latin-1')
    if meth == u'OPTIONS':
        return X.reply(u'HTTP/1.1 204 No Content',
                       [(u'Allow', u', '.join(SUPPORT))], b'')
    if meth not in SUPPORT:
        hd = [(u'Content-Type', u'text/plain; charset=utf-8')]
        if with_allow:
            hd.insert(0, (u'Allow', u', '.join(SUPPORT)))
        return X.reply(u'HTTP/1.1 405 Method Not Allowed', hd, MSG)
    return X.reply(u'HTTP/1.1 200 OK', [], TEXT)

实跑输出:

--------------------------------------------------------------------
Allow:这个资源认哪几个方法
--------------------------------------------------------------------
  先用 OPTIONS 问一句「你都会什么」:
    HTTP/1.1 204 No Content
    Allow: GET, HEAD, OPTIONS

  然后故意用 DELETE 去撞,看 405 里带不带 Allow:
    带 Allow = False → HTTP/1.1 405 Method Not Allowed  响应 133 字节  Allow:(没有)
    带 Allow = True  → HTTP/1.1 405 Method Not Allowed  响应 160 字节  Allow:GET, HEAD, OPTIONS

  两组的差别只有 27 字节(133 对 160),但后果不是「省几个字节」:
  没有 Allow 的 405 只是在说「你错了」,客户端不知道该改成哪个方法;
  带上 Allow,客户端才知道 GET 能用、HEAD 能用,于是能自己换一条路重试。

  Allow 也能出现在 200 里(提前告知),但最常见的位置就是 405 和 OPTIONS。

OPTIONS 那一组回 204,正文 0 字节,首部里一行 Allow: GET, HEAD, OPTIONS。 这就是「问一句答一句」,比试探着发请求便宜得多。 DELETE 那两组都回 405,区别是 133 字节和 160 字节。 27 字节换的是客户端能不能自己纠错。这个比例在任何规模下都划算。 顺带说一句:405 和 501 不一样。405 是「这个方法我认识,但这个资源不支持」; 501 是「这个方法我压根没实现」。

30 秒实验

  1. 把 SUPPORT 改成只留 GET,看 OPTIONS 的响应变成什么样。
  2. 发 PATCH 而不是 DELETE,看 405 里的 Allow 是不是一样。
  3. 想一想:为什么 501 里不该带 Allow?

常见误区

  1. 「405 不带 Allow 也行」——规范要求必须带,不带客户端没法纠错。
  2. 「Allow 是全站支持的方法」——是这一个资源支持的方法。
  3. 「Allow 里的方法一定成功」——只表示语义上接受,权限和状态另算。
自测三问(点开对答案) 1. 哪个状态码必须带 `Allow`? —— 405 Method Not Allowed。 2. `Allow` 和 `Access-Control-Allow-Methods` 有什么区别? —— 前者是 HTTP 本身的,后者是 CORS 专用的,两者常一起出现在预检响应里。 3. 405 和 501 怎么区分? —— 405 是方法认识但资源不支持;501 是方法根本没实现。

方法说完了,接下来是正文本身:先看它被怎么压过,再看它是哪国话。

相关笔记