Skip to main content
 首页 » 编程设计

python之Django Rest Framework 不像标准 POST 请求那样解码 Multipart 请求中的 JSON 字段

2025年04月02日40xiaohuochai

当我使用 application/json 的 Content-Type 提交 POST 请求时,我在服务器上的数据被解码为 native Python - JSON对象以字典的形式呈现,数组以数组的形式呈现等等。太棒了。
但是,当我对我的 API 执行多部分发布请求时,它当然也包含一个文件,任何包含 JSON/对象的字段都不会在服务器上解码,我只剩下需要自己解码的字符串。我的应用程序的性质意味着我不能总是知道我将要获得哪些字段。

我的问题是 - 我如何提交包含文件的多部分请求,同时保留 DRF 在某些字段中解码 JSON 数据的能力? 我试过一起使用所有 3 个主要解析器,但没有用(通过将 API View 的 parser_classes 设置为它们: parser_classes = (MultiPartParser, JSONParser, FormParser)

以下是发送的一些示例请求(通过 Chrome 的开发工具):

Standard Post(非多部分,无文件): {"first_name":"Test","last_name":"Shmest","email":[{"channel_type":"Email","value":"test@example.com","name":null,"default":false}],"company":{"position":"Manager","id":"735d2b5f-e032-4ca8-93e4-c7773872d0cc","name":"The Compapa"},"access": {"private":true,"users":[10,1]},"description":"好人!!","address":{"city":"三凡凡佛","zip":"39292""country":"United States of America","state":"CA","map_url":null,"country_code":"US","address":"123 This street"},"phone":[ {“默认”:false,“类型”:“电话”,“id”:“70e2b437-6841-4536-9acf-f6a55cc372f6”,“值”:“+141512312345”,“名称”:null}],“位置":"","部门":"","主管":"","助理":"","推荐人":"","状态":"","来源":"","类别":"Lead","do_not_call":false,"do_not_text":false,"do_not_email":false,"birthday":null,"identifier":""} DRF 可以很好地读取此有效负载,并且所有值都设置为其 native 等效值。

包含文件的多部分,其中一个字段是 JSON 编码的对象:

------WebKitFormBoundaryPfKUrmBd9vRwp5Rb 
Content-Disposition: form-data; name="file"; filename="image.png" 
Content-Type: image/png 
 
 
------WebKitFormBoundaryPfKUrmBd9vRwp5Rb 
Content-Disposition: form-data; name="first_name" 
 
Test 
------WebKitFormBoundaryPfKUrmBd9vRwp5Rb 
Content-Disposition: form-data; name="last_name" 
 
Shmest 
------WebKitFormBoundaryPfKUrmBd9vRwp5Rb 
Content-Disposition: form-data; name="email" 
 
[{"channel_type":"Email","value":"test@example.com","name":null,"default":false}] 
------WebKitFormBoundaryPfKUrmBd9vRwp5Rb 
Content-Disposition: form-data; name="company" 
 
{"position":"Manager","id":"735d2b5f-e032-4ca8-93e4-c7773872d0cc","name":"The Compapa"} 
------WebKitFormBoundaryPfKUrmBd9vRwp5Rb 
Content-Disposition: form-data; name="access" 
 
{"private":true,"users":[10,1]} 
------WebKitFormBoundaryPfKUrmBd9vRwp5Rb 
Content-Disposition: form-data; name="description" 
 
Nice guy!! 
------WebKitFormBoundaryPfKUrmBd9vRwp5Rb 
Content-Disposition: form-data; name="address" 

我正在寻找的是在我深入手动解码所有字段以检查它们是否为 JSON 之前,看看是否有某种方法可以像在常规 POST 中一样在多部分请求中自动解码 JSON .在发出请求之前,我将获得的大多数字段对我来说都是未知的,因为每个用户可能具有不同的字段组合。

请您参考如下方法:

我创建了一个新的解析器对象来处理这种包含 JSON 字段的多部分文件上传场景。如果有人需要,代码在下面。

import json 
from rest_framework.parsers import BaseParser, DataAndFiles 
from django.conf import settings 
from django.http.multipartparser import MultiPartParser as DjangoMultiPartParser, MultiPartParserError 
from django.utils import six 
from rest_framework.exceptions import ParseError 
 
 
class MultiPartJSONParser(BaseParser): 
    """ 
    Parser for multipart form data which might contain JSON values 
    in some fields as well as file data. 
    This is a variation of MultiPartJSONParser, which goes through submitted fields 
    and attempts to decode them as JSON where a value exists. It is not to be used as a replacement 
    for MultiPartParser, only in cases where MultiPart AND JSON data are expected. 
    """ 
    media_type = 'multipart/form-data' 
 
    def parse(self, stream, media_type=None, parser_context=None): 
        """ 
        Parses the incoming bytestream as a multipart encoded form, 
        and returns a DataAndFiles object. 
        `.data` will be a `QueryDict` containing all the form parameters, and JSON decoded where available. 
        `.files` will be a `QueryDict` containing all the form files. 
        """ 
        parser_context = parser_context or {} 
        request = parser_context['request'] 
        encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET) 
        meta = request.META.copy() 
        meta['CONTENT_TYPE'] = media_type 
        upload_handlers = request.upload_handlers 
 
        try: 
            parser = DjangoMultiPartParser(meta, stream, upload_handlers, encoding) 
            data, files = parser.parse() 
            for key in data: 
                if data[key]: 
                    try: 
                        data[key] = json.loads(data[key]) 
                    except ValueError: 
                        pass 
            return DataAndFiles(data, files) 
        except MultiPartParserError as exc: 
            raise ParseError('Multipart form parse error - %s' % six.text_type(exc)) 

解析器可以像其他任何东西一样在 API View 中使用:

parser_classes = (MultiPartJSONParser, JSONParser, FormParser)