aiohttp:当请求状态码不是 2XX 时获取服务器的响应

aiohttp: Getting a server's response when the request status code is not 2XX

我正在使用 aiohttp 进行异步 http 请求,当请求 returns 出现 4XX 错误时,我不知道如何从服务器获取响应。

    async def login(self, username: str, password: str) -> None:
        ...
        async with aiohttp.ClientSession(headers=self._headers) as session:
            async with session.post(route, data=data, headers=self._headers) as resp:
                if resp.ok:
                    response = await resp.json()
                    self._headers['Authorization'] = 'Bearer ' + response['access_token']
                else:
                    response = await resp.json()
                    raise InvalidGrant(response)

如果响应 returns 一个 2XX 代码,使用 resp.json() 就可以正常工作,但是当它 returns 一个 4XX 错误(在本例中为 400)时,它会引发a aiohttp.client_exceptions.ClientConnectionError 并且不让我得到服务器发送的响应(这是我需要的,因为服务器 returns 某种错误消息比 Bad Request 更具描述性)。如果请求不成功,是否无法通过 aiohttp 获得响应?

出现此问题的原因是 response.ok 的副作用。在旧版本的 aiohttp(3.7 及更低版本)中,response.ok 调用了 response.raise_for_status(),它关闭了 TCP 会话并导致无法再读取服务器的响应。

要解决此问题,您只需将 response = await resp.json() 移动到 response.ok 行上方,这样您就可以预先保存响应。例如:

    async def login(self, username: str, password: str) -> None:
        ...
        async with aiohttp.ClientSession(headers=self._headers) as session:
            async with session.post(route, data=data, headers=self._headers) as resp:
                response = await resp.json()
                if resp.ok:
                    self._headers['Authorization'] = 'Bearer ' + response['access_token']
                else:
                    raise InvalidGrant(response)

此问题已在 aiohttp 3.8 中修复,但是:https://github.com/aio-libs/aiohttp/pull/5404