使用requests向Tastypie接口发送含JSON和zip的multipart请求报错如何解决
问题结论
首先明确:可以通过multipart/form-data内容类型同时传输JSON数据与zip文件,你遇到的报错均为代码逻辑错误导致,和协议本身的能力无关。
报错原因
- 服务端
utf-8解码报错:你直接对整个multipart/form-data请求的request.body做JSON解析,该请求体同时包含JSON文本段和zip二进制内容,不是纯JSON字符串,直接解析必然失败。 - 客户端
无可用反序列化方法报错:Tastypie默认的Serializer未注册multipart/form-data对应的格式映射,无法识别该内容类型。
修复方案
1. 服务端先添加自定义Serializer支持multipart格式
from tastypie.serializers import Serializer import json class CustomSerializer(Serializer): # 扩展支持multipart格式 formats = Serializer.formats + ['multipart'] content_types = dict(Serializer.content_types, **{ 'multipart': 'multipart/form-data' })
2. 修正MultipartResource的反序列化逻辑
class MultipartResource(object): class Meta: # 指定使用自定义Serializer,解决无反序列化方法的问题 serializer = CustomSerializer() def deserialize(self, request, data, format=None): try: if not format: format = request.META.get('CONTENT_TYPE', 'application/x-www-form-urlencoded') if format == 'application/x-www-form-urlencoded': return request.POST if 'multipart' in format: data = request.POST.copy() data.update(request.FILES) # 修正:从POST的JSON字段取内容解析,不要直接解析整个request.body payload = json.loads(data.get('JSON', '{}')) # 注意和客户端传的文件key保持一致,客户端传的是Console不是ADB zip_obj = TestZipModel() zip_obj.ADB = request.FILES['Console'] zip_obj.save() # 解析后的业务参数放入data,供后续逻辑调用 data['payload'] = payload return data except Exception as e: print("Error {}".format(e)) return super(MultipartResource, self).deserialize(request, data, format) def obj_create(self, bundle, request=None, **kwargs): pass
3. 客户端代码无需大幅修改,仅需注意字段名和服务端对齐即可。
更优实现方案
如果你的业务参数是扁平的简单结构,无需嵌套,可以直接用普通表单字段传参数,省去JSON序列化/反序列化步骤,代码更简洁:
客户端修改
def sendLogs(): url = "api/ziprel/" # 直接用data参数传业务参数,无需包装为JSON payload = {"param_1": "value_1","param_2": "value_2"} files = { 'Console': (os.path.basename('/home/pi/Desktop/Console.zip'), open('/home/pi/Desktop/Console.zip','rb'), 'application/zip') } # requests同时传data和files时会自动使用multipart/form-data格式 r = requests.post(url, data=payload, files=files) print(r.content)
服务端适配
不需要再解析JSON,直接从request.POST['param_1']、request.POST['param_2']即可取到业务参数,适合大部分简单场景。如果你的业务参数是复杂嵌套结构,仍可以使用之前传JSON字段的方案。
内容的提问来源于stack exchange,提问作者Toby
相关产品推荐
相关产品推荐

