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方法- 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)
复制代码 来源:豆瓜网用户自行投稿发布,如果侵权,请联系站长删除 |