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,如下图:

Django Rest Framework源码剖析(五)-----解析器详解编程语言

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

Django Rest Framework源码剖析(五)-----解析器详解编程语言

 

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表单数据

Django Rest Framework源码剖析(五)-----解析器详解编程语言

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

Django Rest Framework源码剖析(五)-----解析器详解编程语言

 

三、源码剖析

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]

 

原创文章,作者:Maggie-Hunter,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/12446.html

(0)
上一篇 2021年7月19日 14:37
下一篇 2021年7月19日 14:40

相关推荐

发表回复

登录后才能评论