【DRF-08】rest-framework之解析器

  • 1.解析器的作用

    • 根据请求头 content-type 选择对应的解析器对请求体内容进行处理。
    • 有application/json,x-www-form-urlencoded,form-data等格式, 可以自己自行配置支持或者不支持哪种格式, 一般在实际的生产环境中用json一种数据格式进行数据交互就够了, 如果需要form-data来进行文件处理,再将该格式加入到解析器中即可
  • 2.全局使用解析器

# setting.py
REST_FRAMEWORK = {
    "DEFAULT_VERSION":"v1",
    "ALLOWED_VERSIONS":["v1","v2"],
    "VERSION_PARAM":"version",

    "DEFAULT_PARSER_CLASSES":[
        "rest_framework.parsers.JSONParser",
        "rest_framework.parsers.FormParser",
        ]
}

# 视图
class ParserView(APIView):
    # parser_classes = [FileUploadParser]
    def post(self,request,*args,**kwargs):
        print(request.content_type)
        
        """
        1. 获取用户请求
        2. 获取用户请求体
        3. 根据用户请求头 和 parser_classes = [JSONParser,FormParser,] 中支持的请求头进行比较
        4. JSONParser对象去请求体
        5. request.data
		"""
        
        print(request.data)
        return HttpResponse("解析器")
  • 3.不同解析器与传参的对应关系

  • 4.解析器源码流程

    • 4.1:首先还是走dispatch-->>initialize_request方法中,request中封装了解析器对象,从全局配置中获取
def initialize_request(self, request, *args, **kwargs):
    """
    Returns the initial request object.
    """
    parser_context = self.get_parser_context(request)

    return Request(
        request,
        parsers=self.get_parsers(),
        authenticators=self.get_authenticators(), #  [Foo(),Bar()]
        negotiator=self.get_content_negotiator(),
        parser_context=parser_context
    )

def get_parsers(self):
    """
    Instantiates and returns the list of parsers that this view can use.
    """
    return [parser() for parser in self.parser_classes]

class APIView(View):
    # The following policies may be set at either globally, or per-view.
    renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
    parser_classes = api_settings.DEFAULT_PARSER_CLASSES
    。。。。
  • 4.2:当视图中调用request.data时,执行request中的data方法
@property
def data(self):
    if not _hasattr(self, '_full_data'):
        self._load_data_and_files()
    return self._full_data
  • 4.3:执行_load_data_and_files方法,调用self._parse()
def _load_data_and_files(self):
    """
    Parses the request content into `self.data`.
    """
    if not _hasattr(self, '_data'):
        self._data, self._files = self._parse()
        if self._files:
            self._full_data = self._data.copy()
            self._full_data.update(self._files)
        else:
            self._full_data = self._data

        # if a form media type, copy data & files refs to the underlying
        # http request so that closable objects are handled appropriately.
        if is_form_media_type(self.content_type):
            self._request._post = self.POST
            self._request._files = self.FILES
  • 4.4:执行self._parse,调用parser = self.negotiator.select_parser(self, self.parsers)
def _parse(self):
    """
    Parse the request content, returning a two-tuple of (data, files)

    May raise an `UnsupportedMediaType`, or `ParseError` exception.
    """
    # 用户提交的请求头的content_type的值
    media_type = self.content_type
    try:
        stream = self.stream
    except RawPostDataException:
        if not hasattr(self._request, '_post'):
            raise
        # If request.POST has been accessed in middleware, and a method='POST'
        # request was made with 'multipart/form-data', then the request stream
        # will already have been exhausted.
        if self._supports_form_parsing():
            return (self._request.POST, self._request.FILES)
        stream = None

    if stream is None or media_type is None:
        if media_type and is_form_media_type(media_type):
            empty_data = QueryDict('', encoding=self._request._encoding)
        else:
            empty_data = {}
        empty_files = MultiValueDict()
        return (empty_data, empty_files)
    # self.parsers(全局配置中的解析器)
    # self 请求对象self.content_type
    parser = self.negotiator.select_parser(self, self.parsers)

    if not parser:
        raise exceptions.UnsupportedMediaType(media_type)

    try:
        parsed = parser.parse(stream, media_type, self.parser_context)
    except Exception:
        # If we get an exception during parsing, fill in empty data and
        # re-raise.  Ensures we don't simply repeat the error when
        # attempting to render the browsable renderer response, or when
        # logging the request or similar.
        self._data = QueryDict('', encoding=self._request._encoding)
        self._files = MultiValueDict()
        self._full_data = self._data
        raise

    # Parser classes may return the raw data, or a
    # DataAndFiles object.  Unpack the result as required.
    try:
        return (parsed.data, parsed.files)
    except AttributeError:
        empty_files = MultiValueDict()
        return (parsed, empty_files)

  • 4.5:执行select_parser,根据每个解析器的media_type与请求的content_type比较,返回对应的parser
def select_parser(self, request, parsers):
    """
    Given a list of parsers and a media type, return the appropriate
    parser to handle the incoming request.
    """
    for parser in parsers:
        if media_type_matches(parser.media_type, request.content_type):
            return parser
    return None
  • 4.6:执行4.4中parsed = parser.parse(stream, media_type, self.parser_context)方法。不同解析器的不同parser方法从而进行解析,如JSONParser
def parse(self, stream, media_type=None, parser_context=None):
    """
    Parses the incoming bytestream as JSON and returns the resulting data.
    """
    parser_context = parser_context or {}
    encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)

    try:
        decoded_stream = codecs.getreader(encoding)(stream)
        parse_constant = json.strict_constant if self.strict else None
        return json.load(decoded_stream, parse_constant=parse_constant)
    except ValueError as exc:
        raise ParseError('JSON parse error - %s' % str(exc))
posted @ 2024-05-28 22:45  Tony_xiao  阅读(27)  评论(0编辑  收藏  举报