使用Python requests库发送带文件和JSON令牌的POST请求时,请求体出现Lorem Ipsum文本且令牌丢失的原因
问题原因分析
你遇到的问题其实是requests库的参数优先级和请求格式冲突导致的,还有个小误解:那段"Lorem Ipsum"文本不是requests自动填充的,它就是你上传的payload.txt文件里的内容——看请求体里的filename="payload.txt",里面的内容完全对应你文件里的文本,只是你没意识到这一点而已。
核心问题在于:当你在requests.post()中同时传入files和json参数时,json参数会被直接忽略。因为一旦你传入files,requests会自动把请求格式设置为multipart/form-data,而json参数只在请求格式为application/json时才会生效,两种格式不能同时使用,所以你的JSON令牌根本没被加入到请求里。
解决方案
有两种常用的方法可以解决这个问题,让你同时发送文件和JSON数据:
方法1:将JSON数据作为表单字段传入
把JSON令牌序列化后,作为multipart/form-data表单中的一个普通字段发送,修改你的代码如下:
import requests import json # 需要导入json模块 SERVER = "http://localhost:3030/" test_token = {'session_uuid': '90f50db9-39b1-476c-9fa4-3b7d2af51ad4', 'client_uuid': '371f291c-2a65-4384-9e01-bf3ee880b777', 'expiration': 'Wed Jun 18 19:20:21 EDT 2022'} def post_upload_endpoint(endpoint, path, payload): try: print("Payload: ", payload) # 将JSON序列化后作为data的一部分,和files一起发送 data = {'token': json.dumps(payload)} r = requests.post(endpoint, files=path, data=data) print("Request body: ", r.request.body) return r.json() except Exception as x: print("Exception: ", x) return False, "Failed: {}".format(endpoint) def main(): path = {'document': open('./payload.txt', 'rb')} post_upload_endpoint(SERVER + "exc/sync", path, test_token) if __name__ == "__main__": main()
这样服务器端就可以从表单的token字段中获取到序列化后的JSON字符串,再解析成JSON对象使用。
方法2:将JSON数据作为文件形式上传(不推荐,仅作参考)
如果你想把JSON也当作一个"文件"上传,可以把它加入到files参数里:
import requests import json SERVER = "http://localhost:3030/" test_token = {'session_uuid': '90f50db9-39b1-476c-9fa4-3b7d2af51ad4', 'client_uuid': '371f291c-2a65-4384-9e01-bf3ee880b777', 'expiration': 'Wed Jun 18 19:20:21 EDT 2022'} def post_upload_endpoint(endpoint, path, payload): try: print("Payload: ", payload) # 把JSON转换成字节流,作为一个虚拟文件加入files path['token'] = ('token.json', json.dumps(payload).encode('utf-8'), 'application/json') r = requests.post(endpoint, files=path) print("Request body: ", r.request.body) return r.json() except Exception as x: print("Exception: ", x) return False, "Failed: {}".format(endpoint) def main(): path = {'document': open('./payload.txt', 'rb')} post_upload_endpoint(SERVER + "exc/sync", path, test_token) if __name__ == "__main__": main()
这种方法需要服务器端支持接收多个文件字段,一般不如第一种方法直观。
补充说明
- 一定要注意:
requests.post()中,json参数和files参数是互斥的,不能同时生效——因为它们对应不同的请求Content-Type。 - 你看到的"Lorem Ipsum"确实来自你自己的
payload.txt,可以打开这个文件确认一下内容,就明白是怎么回事了。
内容的提问来源于stack exchange,提问作者granitdev
相关产品推荐
相关产品推荐

