在后续重试之间更改请求标头

Sor*_*shA 12 python urllib3 python-requests

考虑使用 OAuth 令牌的 http 请求。访问令牌需要作为不记名包含在标头中。但是,如果令牌已过期,则需要再次请求刷新令牌,然后重试。所以自定义重试对象将如下所示:

s = requests.Session()
### token is added to the header here
s.headers.update(token_header)
retry = OAuthRetry(
        total=2,
        read=2,
        connect=2,
        backoff_factor=1,
        status_forcelist=[401],
        method_whitelist=frozenset(['GET', 'POST']),
        session=s
    )
adapter = HTTPAdapter(max_retries=retry)
s.mount('http://', adapter)
s.mount('https://', adapter)
r = s.post(url, data=data)
Run Code Online (Sandbox Code Playgroud)

重试类:

class OAuthRetry(Retry):
    def increment(self, method, url, *args, **kwargs):
        # refresh the token here. This could be by getting a reference to the session or any other way.
        return super(OAuthRetry, self).increment(method, url, *args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

问题是刷新token后,HTTPConnectionPool在调用increment后仍然使用相同的header来发起请求。请参阅:https : //github.com/urllib3/urllib3/blob/master/src/urllib3/connectionpool.py#L787。尽管池的实例以增量方式传递,但更改那里的标头不会影响调用,因为它使用标头的本地副本。

这似乎是一个用例,应该经常出现请求参数在重试之间更改。

有没有办法在两次后续重试之间更改请求标头?

yao*_*o99 5

不,在当前版本的 Requests(2.18.4) 和 urllib3(1.22) 中。

\n

openurl重试最终由urllib3处理。并且通过跟踪整个函数的代码,重试之间没有接口可以更改headers

\n

并且动态改变headers不应该被视为解决方案。来自文档

\n
\n

headers \xe2\x80\x93 要发送的自定义标头的字典,例如 User-Agent、If-None-Match 等。如果 None,则使用池标头。如果提供,这些标头将完全替换任何特定于池的标头。

\n
\n

headers是传递给函数的参数。并且不保证通过后不会被复制。尽管在当前版本的 urllib3 中,openurl不复制headers,但任何基于更改的解决方案都headers被认为是hacky,因为它基于实现而不是文档。

\n

一种解决方法

\n

中断一个函数并编辑它正在使用的一些版本是非常危险的

\n

一种简单的解决方案是检查响应状态并在需要时重试,而不是向 urllib3 中注入某些内容。

\n
r = s.post(url, data=data)\nif r.status_code == 401:\n    # refresh the token here.\n    r = s.post(url, data=data)\n
Run Code Online (Sandbox Code Playgroud)\n

为什么原来的方法行不通?

\n

prepare_headers请求在将标头发送到 urllib3 之前复制标头。因此 urllib3 在重试时使用编辑之前创建的副本。

\n

  • 这是一种解决方案,但不是可扩展的。这意味着每个 api 调用都必须像这样重试。所有这些都需要单独测试,本质上这是编写您自己的库来处理请求之间的令牌刷新。 (4认同)