python 3.7 urllib.request 不遵循重定向 URL

cal*_*one 3 python http urllib http-redirect http-status-code-307

我正在使用带有 urllib 的 Python 3.7。一切工作正常,但当它收到 http 重定向请求(307)时,它似乎没有自动重定向。

这是我得到的错误:

ERROR 2020-06-15 10:25:06,968 HTTP Error 307: Temporary Redirect
Run Code Online (Sandbox Code Playgroud)

我必须用 try- except 来处理它,并手动向新位置发送另一个请求:它工作正常,但我不喜欢它。

这些是我用来执行请求的代码片段:

      req = urllib.request.Request(url)
      req.add_header('Authorization', auth)
      req.add_header('Content-Type','application/json; charset=utf-8')
      req.data=jdati  
      self.logger.debug(req.headers)
      self.logger.info(req.data)
      resp = urllib.request.urlopen(req)
Run Code Online (Sandbox Code Playgroud)

url 是一个 https 资源,我设置了一个带有一些授权信息和内容类型的标头。req.data 是一个 JSON

从 urllib 文档中我了解到重定向是由库本身自动执行的,但它对我不起作用。它总是引发 http 307 错误并且不遵循重定向 URL。我还尝试使用指定默认重定向处理程序的开启器,但结果相同

  opener = urllib.request.build_opener(urllib.request.HTTPRedirectHandler)          
  req = urllib.request.Request(url)
  req.add_header('Authorization', auth)
  req.add_header('Content-Type','application/json; charset=utf-8')
  req.data=jdati  
  resp = opener.open(req)         
Run Code Online (Sandbox Code Playgroud)

可能是什么问题呢?

met*_*ter 7

重定向未自动完成的原因已由您在评论部分的讨论中真正正确地确定。具体而言,RFC 2616 第 10.3.8 节指出:

如果收到 307 状态代码来响应除 GET 或 HEAD 之外的请求,则用户代理不得自动重定向请求,除非用户可以确认,因为这可能会更改发出请求的条件。

回到问题 - 假设data已分配,这会自动导致get_method返回POST(根据此方法的实现方式),并且由于请求方法是POST,并且响应代码是307HTTPError因此按照上述规范提出了 an 。在 Python 的上下文中urllib,模块的这个特定部分urllib.request会引发异常。

为了进行实验,请尝试以下代码:

import urllib.request
import urllib.parse


url = 'http://httpbin.org/status/307'
req = urllib.request.Request(url)
req.data = b'hello'  # comment out to not trigger manual redirect handling
try:
    resp = urllib.request.urlopen(req)
except urllib.error.HTTPError as e:
    if e.status != 307:
        raise  # not a status code that can be handled here
    redirected_url = urllib.parse.urljoin(url, e.headers['Location'])
    resp = urllib.request.urlopen(redirected_url)
    print('Redirected -> %s' % redirected_url)  # the original redirected url 
print('Response URL -> %s ' % resp.url)  # the final url
Run Code Online (Sandbox Code Playgroud)

按原样运行代码可能会产生以下结果

Redirected -> http://httpbin.org/redirect/1
Response URL -> http://httpbin.org/get 
Run Code Online (Sandbox Code Playgroud)

请注意,后续重定向get是自动完成的,因为后续请求是一个GET请求。注释掉req.data赋值行将导致缺少“重定向”输出行。

在异常处理块中需要注意的其他值得注意的事情e.read()可以用来检索服务器生成的响应正文作为响应的一部分HTTP 307(自从data发布以来,响应中可能有一个可以处理的短实体?),以及这urljoin是必需的,因为Location标头可能是后续资源的相对 URL(或者只是缺少主机)。

另外,出于兴趣(并且出于链接目的),这个具体问题之前已经被问过多次,我很惊讶他们从未得到任何答案,如下: