美文网首页
解析器源码流程

解析器源码流程

作者: 小吉头 | 来源:发表于2020-07-21 13:24 被阅读0次

django中的请求解析

满足下面两个要求,request.body的值才会解析到request.POST中
1、请求头中:Content-Type:application/x-www-form-urlencoded
2、请求体中:a=1&b=2&c=3这样的格式
form表达和ajax默认是满足上面的要求,所以可以在request.POST中取到值
如果请求头是application/json,需要自己去request.body中取值解析,不会自动解析到request.POST中

解析器模块

解析器的作用就是对请求体进行解析
rest_framework.parsers是restframework提供的解析器模块,其他权限类都要继承BaseParser

parsers属性定义

dispatch()方法中,有下面两段关键代码:

...
#封装Request对象
request = self.initialize_request(request, *args, **kwargs)
...
#认证、权限、限流验证
self.initial(request, *args, **kwargs)
...

initialize_request()方法中封装了新的Request对象

class APIView(View):
    ...
    parser_classes = api_settings.DEFAULT_PARSER_CLASSES#django settings.py中定义DEFAULT_PARSER_CLASSES会覆盖restframework settings.py中的定义
    ...

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

    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(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )

并且增加了parsers属性,self.parser_classesself指向IndexView实例,先去IndexView中查找,如果未定义再去父类中查找
本例settings.py定义:

REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES':['rest_framework.parsers.JSONParser','rest_framework.parsers.FormParser']
}

所以parsers=[JSONParser(),FormParser]

解析流程

只有调用request.data才会根据请求的content_type值,匹配上面获取到的解析器对象,然后去解析请求体

#rest_framework negotiation.py中DefaultContentNegotiation类
    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):#第二个参数是request对象的content_type属性
                return parser
        return None

#rest_framework request.py中Request类
    @property
    def content_type(self):
        meta = self._request.META
        return meta.get('CONTENT_TYPE', meta.get('HTTP_CONTENT_TYPE', ''))

    def _parse(self):
        """
        Parse the request content, returning a two-tuple of (data, files)

        May raise an `UnsupportedMediaType`, or `ParseError` exception.
        """
        media_type = self.content_type #获取请求的content_type 
        
        ...
       #self.parsers=[JSONParser(),FormParser]
        parser = self.negotiator.select_parser(self, self.parsers) #根据content_type值选择对应的解析器

        if not parser:
            raise exceptions.UnsupportedMediaType(media_type)

        try:
            parsed = parser.parse(stream, media_type, self.parser_context) #开始解析请求体数据
        except Exception:
          ...

        # 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)

    def _load_data_and_files(self):
        """
        Parses the request content into `self.data`.
        """
        if not _hasattr(self, '_data'):
            self._data, self._files = self._parse()
            ...

    @property
    def data(self):
        if not _hasattr(self, '_full_data'):
            self._load_data_and_files()
        return self._full_data

以JSONParser类为例,查看实现源码:

class JSONParser(BaseParser):
    """
    Parses JSON-serialized data.
    """
    media_type = 'application/json'
    renderer_class = renderers.JSONRenderer
    strict = api_settings.STRICT_JSON

    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))

用postman发送请求,请求头Content-Type:application/json,请求体:{"name":"tom","age":12}
JSONParser调用parse()方法解析,json.load(decoded_stream, parse_constant=parse_constant)将json字符串读取转换成字典

全局配置

可以在django项目的settings.py中设置REST_FRAMEWORK变量,配置全局解析器:

REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES':['rest_framework.parsers.JSONParser','rest_framework.parsers.FormParser']
}

局部配置

如果只是某个视图使用,可以在类中定义属性,比如:parser_classes = [JSONParser,FormParser]

相关文章

网友评论

      本文标题:解析器源码流程

      本文链接:https://www.haomeiwen.com/subject/dycvcktx.html