zoukankan      html  css  js  c++  java
  • Django Rest Framework源码剖析(五)-----解析器

    一、简介

    解析器顾名思义就是对请求体进行解析。为什么要有解析器?原因很简单,当后台和前端进行交互的时候数据类型不一定都是表单数据或者json,当然也有其他类型的数据格式,比如xml,所以需要解析这类数据格式就需要用到解析器(也可以将请求体拿到,然后利用其他模块进行解析)。

    二、基本使用

    1.json解析器

    同样以订单视图为例,添加json解析器,如下:

    from rest_framework.versioning import URLPathVersioning
    from rest_framework.parsers import JSONParser
    class UserView(APIView):
        '''查看用户信息'''
        parser_classes = [JSONParser,]
        versioning_class =URLPathVersioning
    
        def get(self,request,*args,**kwargs):
            res={"name":"wd","age":22}
            return JsonResponse(res,safe=True)
        
        
        def post(self,request,*args,**kwargs):
            print(request.data) #获取解析后的请求结果
            return JsonResponse({"success":"ok"}, safe=True)

    使用postman向http://127.0.0.1:8000/api/v1/user视图发送json数据,注意请求头必须是application/json,如下图:

    查看post结果(结果直接是json格式):

    2.form表单解析器

    视图

    from rest_framework.versioning import URLPathVersioning
    from rest_framework.parsers import JSONParser,FormParser
    class UserView(APIView):
        '''查看用户信息'''
        parser_classes = [JSONParser,FormParser]
        ##JSONParser,解析头信息Content-Type:application/json,的json数据
        ##FormParser,解析头信息Content-Type:x-www-form-urlencoded数据
        versioning_class =URLPathVersioning
    
        def get(self,request,*args,**kwargs):
            res={"name":"wd","age":22}
            return JsonResponse(res,safe=True)
    
    
        def post(self,request,*args,**kwargs):
            print(request.data) #获取解析后的请求结果
            return JsonResponse({"success":"ok"}, safe=True)

    使用postman发送form表单数据

    后台接受,并且结果已经转化为QueryDict类型了

    三、源码剖析

    1.根据以上示例,梳理解析器解析数据流程

    • 获取用户请求
    • 获取用户请求体
    • 根据用户请求头信息和parase_classes=[...],中的请求头进行比较,匹配上请求头就使用该解析器处理
    • 解析器从请求体中拿数据进行处理,处理完成之后将结果返回给request.data

    2.源码剖析:

    同样和权限源码流程一样,请求进来,先执行APIView的dispatch方法,以下是源码,分析请看注解

    dispatch方法:

    def dispatch(self, request, *args, **kwargs):
            """
            `.dispatch()` is pretty much the same as Django's regular dispatch,
            but with extra hooks for startup, finalize, and exception handling.
            """
            self.args = args
            self.kwargs = kwargs
            #对原始request进行加工,丰富了一些功能
            #Request(
            #     request,
            #     parsers=self.get_parsers(),
            #     authenticators=self.get_authenticators(),
            #     negotiator=self.get_content_negotiator(),
            #     parser_context=parser_context
            # )
            #request(原始request,[BasicAuthentications对象,])
            #获取原生request,request._request
            #获取认证类的对象,request.authticators
            #1.封装request
            request = self.initialize_request(request, *args, **kwargs)
            self.request = request
            self.headers = self.default_response_headers  # deprecate?
    
            try:
                self.initial(request, *args, **kwargs)
    
                # Get the appropriate handler method
                if request.method.lower() in self.http_method_names:
                    handler = getattr(self, request.method.lower(),
                                      self.http_method_not_allowed)
                else:
                    handler = self.http_method_not_allowed
    
                response = handler(request, *args, **kwargs)
    
            except Exception as exc:
                response = self.handle_exception(exc)
    
            self.response = self.finalize_response(request, response, *args, **kwargs)
            return self.response

    执行initialize_request()方法,在该方法中,get_parsers用于获取解析器,并被封装到request.parsers中。

        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(), #获取所有的解析器,封装到request.parsers中
                authenticators=self.get_authenticators(),
                negotiator=self.get_content_negotiator(),
                parser_context=parser_context
            )

    get_parsers()源码,和认证、权限一样,解析器采用列表生成式返回解析器对象的列表,所以示例中定义解析器的变量是parser_classes:

      def get_parsers(self):
            """
            Instantiates and returns the list of parsers that this view can use.
            """
            return [parser() for parser in self.parser_classes] #列表生成式,返回解析器对象

    self.praser_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  #解析器全局配置
        authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
        throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
        permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
        content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
        metadata_class = api_settings.DEFAULT_METADATA_CLASS
        versioning_class = api_settings.DEFAULT_VERSIONING_CLASS

    当调用request.data获取请求数据时候将使用解析器,下面是request.data源码:

     @property
        def data(self):
            if not _hasattr(self, '_full_data'):
                self._load_data_and_files()   #执行_load_data_and_files(),获取请求体数据获取文件数据
            return self._full_data

    执行self._load_data_and_files(),获取请求数据或者文件数据,self._load_data_and_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()  #执行self_parse(),获取解析器,并对content_type进行解析,选择解析器,返回数据
                if self._files:                #判断文件流数据,存在则加入到self._full_data(也就是我们的request.data)中
                    self._full_data = self._data.copy()    ,
                    self._full_data.update(self._files)
                else:
                    self._full_data = self._data           #不存在将无文件流的解析完成的数据赋值到self._full_data(request.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

    执行self._prase()方法,获取解析器,并对请求的Content-Type进行解析,选择解析器,返回解析后的数据,以下是self._prase源码:

        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
            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)
    
            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) #执行解析器的parse方法(从这里可以看出每个解析器都必须有该方法),对请求数据进行解析
            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)     #返回解析结果,元祖,解析后的数据在parsed.data(在load_data_and_files中使用self._data和self._files进行接受),
                                    文件数据在parsed.files中
    except AttributeError: empty_files = MultiValueDict() return (parsed, empty_files)

    以上就是整个django rest framework 解析器源码,下面我们来看看示例中json解析器的源码,说明请看注解:

    class JSONParser(BaseParser):
        """
        Parses JSON-serialized data.
        """
        media_type = 'application/json'   #解析的Content-Type类型
        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)  #本质使用json类进行解析
            except ValueError as exc:
                raise ParseError('JSON parse error - %s' % six.text_type(exc))
    四、总结

    1.解析器本质:

    django rest framework解析本质是根据请求头中的Content-Type来实现,不同的类型使用不同的解析器,一个视图可有多个解析器。

    2.使用:

    #全局使用
    REST_FRAMEWORK = {
       
        #解析器
        "DEFAULT_PARSER_CLASSES":["rest_framework.parsers.JSONParser","rest_framework.parsers.FormParser"]
    }
    
    #单一视图使用
    parser_classes = [JSONParser,FormParser]
  • 相关阅读:
    oracle 导入数据时提示只有 DBA 才能导入由其他 DBA 导出的文件
    oracle 常用语句
    android udp 无法收到数据 (模拟器中)
    android DatagramSocket send 发送数据出错
    AtCoder ABC 128E Roadwork
    AtCoder ABC 128D equeue
    AtCoder ABC 127F Absolute Minima
    AtCoder ABC 127E Cell Distance
    CodeForces 1166E The LCMs Must be Large
    CodeForces 1166D Cute Sequences
  • 原文地址:https://www.cnblogs.com/wdliu/p/9128990.html
Copyright © 2011-2022 走看看