DRF解析器

1、概念

解析器(Parsers)是 Django REST Framework 中的重要组件,它负责将传入的请求体内容解析为 Python 数据类型。
解析器的作用

  • 解析请求体内容(如 JSON、表单数据、XML 等)
  • 将原始数据转换为 Python 数据结构
  • 使转换后的数据可在 request.data 中访问

解析器工作流程

  1. 客户端发送请求(包含 Content-Type 头)
  2. DRF 根据 Content-Type 选择合适的解析器
  3. 解析器解析请求体内容(首次访问request.data解析)
  4. 视图可以访问和使用这些数据

2、内置解析器

2.1 BaseParser

基类,所有解释器应该继承该类,并制定media_type属性和覆盖parse方法

 class BaseParser:     media_type = None      def parse(self, stream, media_type=None, parser_context=None):         raise NotImplementedError(".parse() must be overridden.")  

2.1 JSONParser

最常用的解析器,解析 application/json类型的请求体,将 JSON 数据转换为 Python 字典。核心就是json.load

 class JSONParser(BaseParser):     media_type = 'application/json'     renderer_class = renderers.JSONRenderer     strict = api_settings.STRICT_JSON      def parse(self, stream, media_type=None, parser_context=None):         # ...         return json.load(decoded_stream, parse_constant=parse_constant)  

2.2 FormParser

解析 application/x-www-form-urlencoded类型的请求体(典型的 HTML 表单数据),返回 QueryDict 对象

 class FormParser(BaseParser):     media_type = 'application/x-www-form-urlencoded'      def parse(self, stream, media_type=None, parser_context=None):         parser_context = parser_context or {}         encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)         return QueryDict(stream.read(), encoding=encoding)  

2.3 FileUploadParser

FileUploadParser用于将整个请求体内容视为一个单一的文件进行解析。它通常用于API接口,客户端直接将文件内容作为请求体发送,而不使用 multipart/form-data格式。

请求示例

curl -X PUT    http://your-api-domain/upload/sample.jpg   # 文件名可作为URL的一部分   -H 'Content-Type: image/jpeg'              # 设置文件的实际MIME类型   --data-binary '@/path/to/your/file.jpg' 

视图代码实现

 # views.py from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.parsers import FileUploadParser from rest_framework import status  class SimpleFileUploadView(APIView):     # 使用 FileUploadParser     parser_classes = [FileUploadParser]      def put(self, request, filename, *args, **kwargs): # 注意这里常用 PUT 方法         # 整个请求体被解析为一个文件对象,可通过 request.data 获取         uploaded_file = request.data          # 检查是否有文件数据         if not uploaded_file:             return Response(                 {"error": "No file content"},                  status=status.HTTP_400_BAD_REQUEST             )          # 通常文件名可以从URL参数或请求头中获取,这里假设从URL捕获         # 处理文件,例如保存         with open(filename, 'wb+') as destination:             for chunk in uploaded_file.chunks():                 destination.write(chunk)          return Response({             "message": "File uploaded successfully as single content",             "filename": filename,             "size": uploaded_file.size         }, status=status.HTTP_201_CREATED)  

2.4 MultiPartParser

用于解析 multipart/form-data类型的请求内容,这是浏览器通过表单上传文件时使用的编码格式。它能够同时处理普通的表单字段和文件字段。

请求示例

 curl -X POST    http://your-api-domain/upload/    -H 'Content-Type: multipart/form-data'    -F 'username=testuser'    -F 'file=@/path/to/your/file.jpg'  

视图实现示例

# views.py class FileUploadView(APIView):     # 指定该视图使用的解析器类     parser_classes = [MultiPartParser]      def post(self, request, *args, **kwargs):         # request.data 是一个类似字典的对象,包含所有解析后的表单数据和文件         # 获取名为 'file' 的文件对象         uploaded_file = request.data.get('file')         # 获取名为 'username' 的普通文本字段         username = request.data.get('username')          if not uploaded_file:             return Response(                 {"error": "No file provided"},                  status=status.HTTP_400_BAD_REQUEST             )          # 处理文件,例如保存到磁盘         with open(uploaded_file.name, 'wb+') as destination:             for chunk in uploaded_file.chunks():                 destination.write(chunk)          # 返回成功响应         return Response({             "message": "File uploaded successfully",             "filename": uploaded_file.name,             "username": username,             "size": uploaded_file.size         }, status=status.HTTP_201_CREATED)  

3、自定义解释器

创建自定义解析器需要继承 BaseParser 并实现 parse 方法

 from rest_framework.parsers import BaseParser import xml.etree.ElementTree as ET  class XMLParser(BaseParser):     """     自定义 XML 解析器     """     media_type = 'application/xml'  # 处理的 Content-Type          def parse(self, stream, media_type=None, parser_context=None):         """         将 XML 流解析为 Python 字典         """         parser_context = parser_context or {}         encoding = parser_context.get('encoding', 'utf-8')                  try:             # 读取并解析 XML             xml_text = stream.read().decode(encoding)             root = ET.fromstring(xml_text)             return self._xml_to_dict(root)         except ET.ParseError as e:             from rest_framework.exceptions import ParseError             raise ParseError(f'XML parse error - {e}')          def _xml_to_dict(self, element):         """         将 XML 元素转换为字典         """         result = {}         for child in element:             if len(child) == 0:                 result[child.tag] = child.text             else:                 result[child.tag] = self._xml_to_dict(child)         return result  

4、配置使用

4.1 全局配置

在 settings.py 中设置默认解析器,默认为JSONParser,FormParser,MultiPartParser

# settings.py REST_FRAMEWORK = {     'DEFAULT_PARSER_CLASSES': [         'rest_framework.parsers.JSONParser',         'rest_framework.parsers.FormParser',         'rest_framework.parsers.MultiPartParser',     ], } 

4.2 视图级配置

 class FlexibleParserView(APIView):     parser_classes = [JSONParser, FormParser, MultiPartParser]          def post(self, request):         return Response({             "data": request.data,             "content_type": request.content_type         })   @api_view(['POST']) @parser_classes([JSONParser]) def json_only_view(request):     return Response({"data": request.data}) 

5、解析器选择流程

DRF 通过内容协商来选择解析器,也就是根据media_type选择

# rest_framework/request.py  class Request:     def __init__(self, request, parsers=None, authenticators=None, negotiator=None, parser_context=None):         self.parsers = parsers or ()      @property     def data(self):         if not _hasattr(self, '_full_data'):             with wrap_attributeerrors():                 self._load_data_and_files()         return self._full_data            def _load_data_and_files(self):         # 只加载一次         if not _hasattr(self, '_data'):             # 解析数据             self._data, self._files = self._parse()          def _parse(self):         # 获取内容类型         media_type = self.content_type         #  # 选择解析器         parser = self.negotiator.select_parser(self, self.parsers)         # 使用选择的解析器进行解析         parsed = parser.parse(stream, media_type, self.parser_context)         return (parsed.data, parsed.files) 

发表评论

评论已关闭。

相关文章