Python 填坑之——requests.post 方法中 data 参数与 json 参数区别

近期在使用 WxPusher 推送服务,官方文档要求,在发送 POST 请求时应当将数据放入 body 内,并设置 Content-Type 为 application/json。

本人多次使用 data 参数进行请求,均以失败告终。后来换用 json 参数,即刻成功,特此分享记录。

预备知识

Http 协议介绍

HTTP/1.1 协议规定,一个完整的 HTTP 请求分为三个部分:请求行,请求头和请求主体。大概框架如下:

1
2
3
4
<method> <request-URL> <version>
<headers>

<body>

例如:

1
2
3
4
POST http://test.com HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=utf-8

test_a=123&test_b=456

Content-Type 字段介绍

application/x-www-form-urlencoded

这是原生 form 表单,是最常见的编码方式,用途广泛。当 Content-Type 字段被设置为 application/x-www-form-urlencoded 时,body 数据为 key/value 的形式,通过 URL 编码后使用 & 符号进行连接。大部分的服务端都可以支持这种编码方式。

前文中的“示例”采用的就是这种编码方式。

application/json

随着 JSON 规范的流行,这种编码方式被越来越多人采用,原因是这种编码方式可以直接提交结构化的数据,方便数据的处理。

例如:

1
2
3
4
POST http://test.com HTTP/1.1
Content-Type: application/json;charset=utf-8

{"test_a": 123, "test_b": 456}

仔细观察,不难发现,上述两种编码方式最大的不同就体现在 <body> 部分。

前者的提交类似于 Python 中的 str 类型,而后者则是 dict 类型。

requests 库在进行 POST 请求时,如果使用的是 data= 参数,则默认使用 application/x-www-form-urlencoded 编码方式;如果使用 json= 参数,则默认使用 application/json 编码方式。

案例分析

相信看完前文,一大部分小伙伴就已经找到了问题所在,下面结合本次我掉进的“坑”再来分析下。

本次调用 WxPusher 时,我使用了如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import requests

url_postmsg = "https://wxpusher.zjiecode.com/api/send/message"
hea = {
"Content-Type": "application/json"
}
data = {
"appToken": str(_appToken),
"content": str(_content),
"summary": str(_summary),
"contentType": int(_type),
"topicIds": _topicIds,
"uids": _uids,
"url": _origin_url
}
res = requests.post(url=url_postmsg, data=data, headers=hea)

为了满足官方的需求,我特意手动设置了 headers 中的 Content-Typeapplication/json

但是,当我使用 data= 参数时,requests 库默认使用 application/x-www-form-urlencoded 方式对我的数据进行编码。

此时,虽然 headers 中的 Content-Type 仍然是 application/json ,但我所提交的数据就已经不是 json 格式的了,而是前文所述的类似于 str 的文本,导致服务器无法解析数据,从而出错。

The End

希望上述分享能对大家有所帮助,以免再次掉进“两种不同的参数造成数据传输格式不同”的“坑”里。

参考链接: requests 中 post 参数 data 和 json 区别