• 六、django rest_framework源码之解析器剖析


    1 绪论

      网络传输数据只能传输字符串格式的,如果是列表、字典等数据类型,需要转换之后才能使用但是我们之前的rest_framework例子都没有转换就直接可以使用了,这是因为rest_framework自带有一套解析器。

    2 源码分析

      我们重新回到dispatch方法中对请求进行重新封装的initialize_request类,源码如下:

    def initialize_request(self, request, *args, **kwargs):
    
            """
    
            Returns the initial request object.
    
            """
    
            print('initialize_request方法执行:封装request开始')
    
            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
    
            )

      源码中调用了一个get_parsers方法,这个类返回的是项目中配置的所有解析器类实例化对象组成的列表,源码如下:   

     def get_parsers(self):
            return [parser() for parser in self.parser_classes]

      get_parsers方法采用列表生成式的方式遍历了配置的所有解析器类,并进行实例化,与认证、权限等实例化操作一般无二。所以,在重新封装request的时候,rest_framework就把解析器类实例封装进了request中,但是与认证、权限等环节不懂的是,解析操作并不是在dispatch函数执行过程中完成。请看如下代码:

    class QarserView(APIView):
    
        parser_classes = [JSONParser ,]
    
        def post(self , request , *args , **kwargs):
    
            print(request.data)#解析器开始执行
    
            return HttpResponse(request.data)

      也就是说,只有当视图中调用了request.data的时候,解析器才会真正开始运转起来。这里的data是封装在rest_framework的Request中的一个方法,源码如下:

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

      关键是其中的_load_data_and_files方法,如下所示:

    def _load_data_and_files(self):
        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 is_form_media_type(self.content_type):
                self._request._post = self.POST
                self._request._files = self.FILES

      _parse方法:

    def _parse(self):
        media_type = self.content_type#获取用户提交的请求头的content_type
        try:
            stream = self.stream#获取request请求体
        except RawPostDataException:
            if not hasattr(self._request, '_post'):
                raise
            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)#返回一个QueryDict对象,内容为{空
            else:
                empty_data = {}
            empty_files = MultiValueDict()
            return (empty_data, empty_files)
        #media_type不为空时就挑选解析器,用media_type与配置的每个解析类的media_type对比
        parser = self.negotiator.select_parser(self, self.parsers)#得到相匹配的解析器对象
        if not parser:#如果没有相匹配的解析器,抛出异常
            raise exceptions.UnsupportedMediaType(media_type)
        try:#执行解析器实例对象的parse方法
            parsed = parser.parse(stream, media_type, self.parser_context)
        except Exception:
            self._data = QueryDict('', encoding=self._request._encoding)
            self._files = MultiValueDict()
            self._full_data = self._data
            raise
        try:
            return (parsed.data, parsed.files)#返回解析后的数据
        except AttributeError:
            empty_files = MultiValueDict()
            return (parsed, empty_files)

      在这个_parse方法中,就调用了解析器类的parse方法,下面的是JSONParser中的parse方法:

    def parse(self, stream, media_type=None, parser_context=None):
    
        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#转化为json数据
    
            return json.load(decoded_stream, parse_constant=parse_constant)
    
        except ValueError as exc:
    
            raise ParseError('JSON parse error - %s' % six.text_type(exc))

    parse解析取得json数据后,层层网上返回,最终在request.data中输出json数据。

    3 配置方法

      局部配置:

      只用到的试图类中加入以下代码:

    parser_classes = [JSONParser ,]

      全局配置:

      在settings.py文件中配置

    REST_FRAMEWORK = {
    
        "DEFAULT_PARSER_CLASSES":[
    
            'rest_framework.parsers.JSONParser' ,
    
            'rest_framework.parsers.FormParser' ,
    ]
    
    }
  • 相关阅读:
    万能还是万恶的花括号?(PHP)
    深入理解PHP对象赋值
    关联容器常用用法
    顺序容器常用用法
    浏览器安全:35 | 安全沙箱:页面和系统之间的隔离墙
    vue 重学笔记一
    Vue 中 父子组件的生命周期钩子函数的执行顺序
    浏览器中的网络:31 | HTTP/3:甩掉 TCP、TLS 的包袱,构建高效网络
    浏览器安全:34 | CSRF攻击:陌生链接不能随便点
    webpack 打包工具,loader 和 plugin 是什么,是如何实现的
  • 原文地址:https://www.cnblogs.com/chenhuabin/p/9988724.html
Copyright © 2020-2023  润新知