在REST API的HTTP/1.0请求中省略Accept*/*标头是一个错误吗?

Ray*_*ger 15 python rest curl http http-headers

我正在尝试确定在制作简单的REST API请求时,Python的urllib.urlopen()函数是否遗漏了HTTP Accept标头.

Facebook的图形API似乎注意到是否存在头部与否:

GET /zuck HTTP/1.0
Host: graph.facebook.com
Accept: */*
Run Code Online (Sandbox Code Playgroud)

如果没有接受头,返回的内容类型application/json; charset=UTF-8text/javascript; charset=UTF-8.这可能是Facebook的REST API中的错误,或者它可能是对缺少的接受标头的合法响应.

我注意到默认情况下使用curl等命令行工具Accept: */*:

$ curl -v https://graph.facebook.com/zuck
> GET /zuck HTTP/1.1
> User-Agent: curl/7.30.0
> Host: graph.facebook.com
> Accept: */*
Run Code Online (Sandbox Code Playgroud)

同样,Python请求包Accept: */*用作默认值:

def default_headers():
    return CaseInsensitiveDict({
        'User-Agent': default_user_agent(),
        'Accept-Encoding': ', '.join(('gzip', 'deflate')),
        'Accept': '*/*',
        'Connection': 'keep-alive',
    })
Run Code Online (Sandbox Code Playgroud)

我认为卷曲请求添加默认值是有原因的,但我不确定是什么原因.

HTTP/1.1的RFC 2616说明了这 */* indicates all media types一点if no Accept header field is present, then it is assumed that the client accepts all media types.这似乎表明这Accept: */*是可选的,其遗漏不会产生任何影响.也就是说,Python正在使用HTTP/1.0,并且RFC对于省略标题的效果没有提及.

我想确定最佳实践是包括Accept: */*作为curl请求,或者是否可以省略,就像Python的urllib.urlopen()那样.

这个问题非常重要,因为我可以修复urllib.urlopen(),如果确定它是错误的,或者是否与使用HTTP/1.0通常实现的REST API一起使用有问题:

>>> import httplib
>>> httplib.HTTPConnection.debuglevel = 1
>>> import urllib
>>> u = urllib.urlopen('https://graph.facebook.com/zuck')
send: 'GET /zuck HTTP/1.0\r\nHost: graph.facebook.com\r\nUser-Agent: Python-urllib/1.17\r\n\r\n'
Run Code Online (Sandbox Code Playgroud)

StackOverflow上的相关问题对此问题没有帮助. 在"请求标头"的"客户端"部分下,"接受:*/*"是什么意思?询问是什么*/*意思(我们已经知道它意味着所有媒体类型)并发送没有Accept标头的curl请求?询问如何在curl请求中省略accept头.我的问题集中在你是否应该包括*/*它以及是否是一个忽略它的bug.

Ren*_*rdt 12

RFC声明

Accept request-header字段可用于指定响应可接受的某些媒体类型.

这意味着标题是可选的,因为它说can be used.

正如你所指出的那样,RFC也说:

如果不存在Accept头字段,则假定客户端接受所有媒体类型.

这意味着在两种情况下,省略头部应该被服务器等效地解释为发送Accept: */*客户端acceptes all media types.

有趣的是,facebook的响应在两种情况下都有所不同,但我想这是他们无法正确解释协议.虽然在另一方面,这两个回答显然是对请求的正确回应(我觉得这很有趣).

我对这个问题有一些一般性的想法(也可能有助于bugfix讨论):

  1. 根据Postel Law, Be conservative in what you do, be liberal in what you accept from others (often reworded as "Be conservative in what you send, be liberal in what you accept").您可以决定更精确并明确地添加Accept: */*.你会更准确地帮助服务器,他可能误解了协议(比如facebook可能做的那样),缺少的标题相当于Accept: */*
  2. 只需添加Accept: */*可省略的标题字段,每个请求的网络流量就会增加11字节,这可能会导致性能问题.在Accept: */*请求中默认可能会使开发人员难以将其从标头中删除以节省到11字节.
  3. 规范(或标准)与事实标准之间存在差异.显然,根据规范省略头字段是完美的,另一方面很多库似乎都包含这个,而像facebook API这样的服务行为不同,这可以看作是一个事实上的标准正在创建,你可以跳进循环,成为创造它的一部分.

在说HTTP/1.1时:尽管(1)und(3)说要修复urllib,我可能会遵循规范和性能参数(2)并省略标题.如上所述,facebook在两种情况下的响应都是正确的,因为他们可以将媒体类型设置为他们喜欢的任何类型.(即使这种行为似乎是无意的,很奇怪,而且是错误的)

在说HTTP/1.0时:我会发送接受标头,因为你说它没有在HTTP/1.0 RFC中指定,然后我认为Postel的定律变得更加重要.另一方面,Accept标头在http 1.0中是可选的.The Accept request-header field can be used to indicate a list of media ranges which are acceptable as a response to the request为什么默认设置可选标头?


Ray*_*ger 3

阅读有关代理服务器(如 NGinx 和 Varnish)的信息帮助我弄清楚发生了什么。

虽然标头的存在Accept: */*不会对服务器产生影响,但当响应包含标头时,它可能并且很可能会对代理服务器产生影响Vary: Accept。特别是,允许​​代理服务器针对不同或省略的Accept标头缓存不同的结果。

自从提出这个问题以来,Facebook 已经更新(并关闭)了其 API,但当时,这是导致观察到的影响的场景。出于向后兼容性的原因,Facebook 在收到省略标头或具有类似浏览器的text/javascript; charset=UTF-8请求时使用内容协商和响应。然而,当它收到时,它又变得更加现代了。当代理服务器收到没有接受标头的请求时,它可以给出缓存的响应之一;然而,当它得到 时,它总是给出最后的响应。AcceptAccept: text/html;text/*;*/*Accept: */*application/json; charset=UTF-8Accept: */*

因此,这就是为什么您应该包含Accept: */*标头:如果您这样做,那么缓存代理将始终返回相同的内容类型。如果省略标头,则响应可能会根据上次用户内容协商的结果而有所不同。REST API 客户端往往依赖于每次都返回相同的内容类型。