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

解析器源码流程

作者: 小吉头 | 来源:发表于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