You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何Python requests的PreparedRequest传json时body为bytes、传data为str?

requests库json=参数主动编码请求体为bytes的原因

这个设计是requests基于使用场景、底层依赖要求、跨版本兼容三个维度做的明确选择,核心逻辑如下:

  • 底层传输的硬性要求:HTTP协议在网络中传输的所有内容本质都是字节流,requests依赖的底层HTTP客户端urllib3,要求传入的请求体必须是类bytes类型才能正常发送。
  • 抹平Python版本差异:Python2环境中标准库json.dumps默认返回bytes类型字符串,但Python3环境中json.dumps返回的是Unicode格式的str类型。如果不在JSON处理分支主动做UTF-8编码,Python3环境下构造的请求会在发送阶段触发类型错误。
  • 匹配参数的设计定位:json=参数是requests提供的开箱即用JSON请求构造入口,设计目标就是让用户只需要传入Python可序列化对象,不需要手动处理JSON序列化、字符编码、Content-Type头设置这些重复工作,所以库内部会把所有前置处理做完,最终输出可以直接交给底层传输的标准bytes格式请求体。

和data=参数行为差异的原因

data=参数的定位是通用自定义请求体入口,支持传入字典、字符串、字节、文件对象、迭代器/生成器等多种类型,不会在prepare_body阶段对str类型内容做强制编码:

  • 如果传入str类型内容,requests会在实际发起网络请求的最后阶段,按照请求头中指定的字符集做编码,不会在请求预处理阶段提前转换类型
  • 如果用户传入的是已经编码完成的bytes、文件流、惰性生成器对象,提前执行编码逻辑反而会破坏原始内容,导致请求发送异常

行为复现

In [8]: requests.Request('GET', 'https://httpbin.org/get', json={'a': 5}).prepare().body
Out[8]: b'{"a": 5}' # bytes类型

In [9]: requests.Request('GET', 'https://httpbin.org/get', data=json.dumps({'a': 5})).prepare().body
Out[9]: '{"a": 5}'  # str类型

对应源码佐证

requests.models中prepare_body方法的源码和注释已经直接说明了这段编码逻辑的设计初衷:

def prepare_body(self, data, files, json=None):
    """Prepares the given HTTP body data."""
    body = None
    content_type = None

    if not data and json is not None:
        # urllib3 requires a bytes-like body. Python 2's json.dumps
        # provides this natively, but Python 3 gives a Unicode string.
        content_type = 'application/json'

        try:
          body = complexjson.dumps(json, allow_nan=False)
        except ValueError as ve:
          raise InvalidJSONError(ve, request=self)

        if not isinstance(body, bytes):
            body = body.encode('utf-8') # 主动编码逻辑

内容的提问来源于stack exchange,提问作者nhtrnm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 13:12:32