发送请求之前在aiohttp中的post请求中获取准备好的数据

问题描述 投票:0回答:1

我使用 aiohttp 来发出 post 请求。我需要在发送请求之前获取post请求中准备好的数据。所以我尝试在 aiohttp 中找到一种类似于 requests 库中的方法 prepare 的方法。可以吗?

python python-asyncio aiohttp
1个回答
0
投票

这只是 aiohttp 中的一个概念验证解决方法,用于在发送请求之前获取请求正文,并同时执行操作(例如通常签名和添加签名标头)。

免责声明:我不建议在生产环境(或严肃的场合)使用它。因为它依赖于 aiohttp 中非公开且未记录的函数或属性。如果 aiohttp 的内部实现发生变化,它可能随时崩溃。

import aiohttp
import asyncio
import hashlib
import hmac

SECRET_KEY = '__SECRET_KEY__'


class HMACBodySignClientRequest(aiohttp.ClientRequest):
    def update_body_from_data(self, body) -> None:
        """bad: hook this internal method to get body and inject signature (may fail anytime if internal changes)"""
        super().update_body_from_data(body)

        if body is None:  # use `body` rather than `self.body` is expected.
            return

        body = self.body._value or b''  # bad: access to protected property (may not available)
        signature = hmac.new(SECRET_KEY.encode('utf-8'), body, digestmod=hashlib.sha256)
        self.headers['X-Signature'] = signature.hexdigest()


async def aiohttp_body_sign_workaround():
    url = 'https://httpbin.org/api'
    data = {'foo': 'bar'}
    async with aiohttp.ClientSession(
            request_class=HMACBodySignClientRequest) as session:  # <-- auto add signature via `request_class`
        async with session.post(url=url, json=data) as response:
            print(response.request_info)


asyncio.run(aiohttp_body_sign_workaround())

在aiohttp 3.8.5上测试,在任何其他版本中可能会失败。

请注意,这种方法是一种“黑魔法”,可能仅在某些微不足道的情况下有效,例如小数据或非流请求。使用需要您自担风险。


它是如何工作的?

在aiohttp当前的内部设计中,

_request_class: ClientRequest
上有一个内部属性
ClientSession
,它委托了实际的请求生成。

在某些时候,请求正文字节可能可以获取。

发现在调用

ClientRequest
方法
update_body_from_data()
之后,访问
self.body._value
可能会泄露主体字节。

幸运的是,在使用

_request_class
参数初始化
ClientSession
时可以指定
request_class
属性。这意味着我们可以子类
ClientRequest
并重写/挂钩
update_body_from_data()
方法来添加自定义逻辑(在本例中,将主体的 HMAC 签名添加到标头)。

是的,它依赖于内部细节,所以正如我所说,这只是一个概念验证。

© www.soinside.com 2019 - 2024. All rights reserved.