找回密码
 立即注册
首页 业界区 业界 DRF解析器

DRF解析器

癖艺泣 昨天 13:59
1、概念

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

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

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

2.1 BaseParser

基类,所有解释器应该继承该类,并制定media_type属性和覆盖parse方法
  1. class BaseParser:
  2.     media_type = None
  3.     def parse(self, stream, media_type=None, parser_context=None):
  4.         raise NotImplementedError(".parse() must be overridden.")
复制代码
2.1 JSONParser

最常用的解析器,解析 application/json类型的请求体,将 JSON 数据转换为 Python 字典。核心就是json.load
  1. class JSONParser(BaseParser):
  2.     media_type = 'application/json'
  3.     renderer_class = renderers.JSONRenderer
  4.     strict = api_settings.STRICT_JSON
  5.     def parse(self, stream, media_type=None, parser_context=None):
  6.         # ...
  7.         return json.load(decoded_stream, parse_constant=parse_constant)
复制代码
2.2 FormParser

解析 application/x-www-form-urlencoded类型的请求体(典型的 HTML 表单数据),返回 QueryDict 对象
  1. class FormParser(BaseParser):
  2.     media_type = 'application/x-www-form-urlencoded'
  3.     def parse(self, stream, media_type=None, parser_context=None):
  4.         parser_context = parser_context or {}
  5.         encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
  6.         return QueryDict(stream.read(), encoding=encoding)
复制代码
2.3 FileUploadParser

FileUploadParser用于将整个请求体内容视为一个单一的文件进行解析。它通常用于API接口,客户端直接将文件内容作为请求体发送,而不使用 multipart/form-data格式。
请求示例
  1. curl -X PUT \
  2.   http://your-api-domain/upload/sample.jpg \  # 文件名可作为URL的一部分
  3.   -H 'Content-Type: image/jpeg' \             # 设置文件的实际MIME类型
  4.   --data-binary '@/path/to/your/file.jpg'
复制代码
视图代码实现
  1. # views.py
  2. from rest_framework.views import APIView
  3. from rest_framework.response import Response
  4. from rest_framework.parsers import FileUploadParser
  5. from rest_framework import status
  6. class SimpleFileUploadView(APIView):
  7.     # 使用 FileUploadParser
  8.     parser_classes = [FileUploadParser]
  9.     def put(self, request, filename, *args, **kwargs): # 注意这里常用 PUT 方法
  10.         # 整个请求体被解析为一个文件对象,可通过 request.data 获取
  11.         uploaded_file = request.data
  12.         # 检查是否有文件数据
  13.         if not uploaded_file:
  14.             return Response(
  15.                 {"error": "No file content"},
  16.                 status=status.HTTP_400_BAD_REQUEST
  17.             )
  18.         # 通常文件名可以从URL参数或请求头中获取,这里假设从URL捕获
  19.         # 处理文件,例如保存
  20.         with open(filename, 'wb+') as destination:
  21.             for chunk in uploaded_file.chunks():
  22.                 destination.write(chunk)
  23.         return Response({
  24.             "message": "File uploaded successfully as single content",
  25.             "filename": filename,
  26.             "size": uploaded_file.size
  27.         }, status=status.HTTP_201_CREATED)
复制代码
2.4 MultiPartParser

用于解析 multipart/form-data类型的请求内容,这是浏览器通过表单上传文件时使用的编码格式。它能够同时处理普通的表单字段和文件字段。
请求示例
  1. curl -X POST \
  2.   http://your-api-domain/upload/ \
  3.   -H 'Content-Type: multipart/form-data' \
  4.   -F 'username=testuser' \
  5.   -F 'file=@/path/to/your/file.jpg'
复制代码
视图实现示例
  1. # views.py
  2. class FileUploadView(APIView):
  3.     # 指定该视图使用的解析器类
  4.     parser_classes = [MultiPartParser]
  5.     def post(self, request, *args, **kwargs):
  6.         # request.data 是一个类似字典的对象,包含所有解析后的表单数据和文件
  7.         # 获取名为 'file' 的文件对象
  8.         uploaded_file = request.data.get('file')
  9.         # 获取名为 'username' 的普通文本字段
  10.         username = request.data.get('username')
  11.         if not uploaded_file:
  12.             return Response(
  13.                 {"error": "No file provided"},
  14.                 status=status.HTTP_400_BAD_REQUEST
  15.             )
  16.         # 处理文件,例如保存到磁盘
  17.         with open(uploaded_file.name, 'wb+') as destination:
  18.             for chunk in uploaded_file.chunks():
  19.                 destination.write(chunk)
  20.         # 返回成功响应
  21.         return Response({
  22.             "message": "File uploaded successfully",
  23.             "filename": uploaded_file.name,
  24.             "username": username,
  25.             "size": uploaded_file.size
  26.         }, status=status.HTTP_201_CREATED)
复制代码
3、自定义解释器

创建自定义解析器需要继承 BaseParser 并实现 parse 方法
  1. from rest_framework.parsers import BaseParser
  2. import xml.etree.ElementTree as ET
  3. class XMLParser(BaseParser):
  4.     """
  5.     自定义 XML 解析器
  6.     """
  7.     media_type = 'application/xml'  # 处理的 Content-Type
  8.    
  9.     def parse(self, stream, media_type=None, parser_context=None):
  10.         """
  11.         将 XML 流解析为 Python 字典
  12.         """
  13.         parser_context = parser_context or {}
  14.         encoding = parser_context.get('encoding', 'utf-8')
  15.         
  16.         try:
  17.             # 读取并解析 XML
  18.             xml_text = stream.read().decode(encoding)
  19.             root = ET.fromstring(xml_text)
  20.             return self._xml_to_dict(root)
  21.         except ET.ParseError as e:
  22.             from rest_framework.exceptions import ParseError
  23.             raise ParseError(f'XML parse error - {e}')
  24.    
  25.     def _xml_to_dict(self, element):
  26.         """
  27.         将 XML 元素转换为字典
  28.         """
  29.         result = {}
  30.         for child in element:
  31.             if len(child) == 0:
  32.                 result[child.tag] = child.text
  33.             else:
  34.                 result[child.tag] = self._xml_to_dict(child)
  35.         return result
复制代码
4、配置使用

4.1 全局配置

在 settings.py 中设置默认解析器,默认为JSONParser,FormParser,MultiPartParser
  1. # settings.py
  2. REST_FRAMEWORK = {
  3.     'DEFAULT_PARSER_CLASSES': [
  4.         'rest_framework.parsers.JSONParser',
  5.         'rest_framework.parsers.FormParser',
  6.         'rest_framework.parsers.MultiPartParser',
  7.     ],
  8. }
复制代码
4.2 视图级配置
  1. class FlexibleParserView(APIView):
  2.     parser_classes = [JSONParser, FormParser, MultiPartParser]
  3.    
  4.     def post(self, request):
  5.         return Response({
  6.             "data": request.data,
  7.             "content_type": request.content_type
  8.         })
  9. @api_view(['POST'])
  10. @parser_classes([JSONParser])
  11. def json_only_view(request):
  12.     return Response({"data": request.data})
复制代码
5、解析器选择流程

DRF 通过内容协商来选择解析器,也就是根据media_type选择
  1. # rest_framework/request.py
  2. class Request:
  3.     def __init__(self, request, parsers=None, authenticators=None, negotiator=None, parser_context=None):
  4.         self.parsers = parsers or ()
  5.     @property
  6.     def data(self):
  7.         if not _hasattr(self, '_full_data'):
  8.             with wrap_attributeerrors():
  9.                 self._load_data_and_files()
  10.         return self._full_data      
  11.     def _load_data_and_files(self):
  12.         # 只加载一次
  13.         if not _hasattr(self, '_data'):
  14.             # 解析数据
  15.             self._data, self._files = self._parse()
  16.    
  17.     def _parse(self):
  18.         # 获取内容类型
  19.         media_type = self.content_type
  20.         #  # 选择解析器
  21.         parser = self.negotiator.select_parser(self, self.parsers)
  22.         # 使用选择的解析器进行解析
  23.         parsed = parser.parse(stream, media_type, self.parser_context)
  24.         return (parsed.data, parsed.files)
复制代码
来源:豆瓜网用户自行投稿发布,如果侵权,请联系站长删除

相关推荐

您需要登录后才可以回帖 登录 | 立即注册