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-8变text/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讨论):
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: */*Accept: */*可省略的标题字段,每个请求的网络流量就会增加11字节,这可能会导致性能问题.在Accept: */*请求中默认可能会使开发人员难以将其从标头中删除以节省到11字节.在说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为什么默认设置可选标头?
阅读有关代理服务器(如 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 客户端往往依赖于每次都返回相同的内容类型。