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]
Django Rest Framework源码剖析(五)-----解析器的更多相关文章
- Django Rest Framework源码剖析(八)-----视图与路由
一.简介 django rest framework 给我们带来了很多组件,除了认证.权限.序列化...其中一个重要组件就是视图,一般视图是和路由配合使用,这种方式给我们提供了更灵活的使用方法,对于使 ...
- Django Rest Framework源码剖析(三)-----频率控制
一.简介 承接上篇文章Django Rest Framework源码剖析(二)-----权限,当服务的接口被频繁调用,导致资源紧张怎么办呢?当然或许有很多解决办法,比如:负载均衡.提高服务器配置.通过 ...
- Django Rest Framework源码剖析(四)-----API版本
一.简介 在我们给外部提供的API中,可会存在多个版本,不同的版本可能对应的功能不同,所以这时候版本使用就显得尤为重要,django rest framework也为我们提供了多种版本使用方法. 二. ...
- Django Rest Framework源码剖析(七)-----分页
一.简介 分页对于大多数网站来说是必不可少的,那你使用restful架构时候,你可以从后台获取数据,在前端利用利用框架或自定义分页,这是一种解决方案.当然django rest framework提供 ...
- Django Rest Framework源码剖析(六)-----序列化(serializers)
一.简介 django rest framework 中的序列化组件,可以说是其核心组件,也是我们平时使用最多的组件,它不仅仅有序列化功能,更提供了数据验证的功能(与django中的form类似). ...
- Django Rest Framework源码剖析(二)-----权限
一.简介 在上一篇博客中已经介绍了django rest framework 对于认证的源码流程,以及实现过程,当用户经过认证之后下一步就是涉及到权限的问题.比如订单的业务只能VIP才能查看,所以这时 ...
- Django Rest Framework源码剖析(一)-----认证
一.简介 Django REST Framework(简称DRF),是一个用于构建Web API的强大且灵活的工具包. 先说说REST:REST是一种Web API设计标准,是目前比较成熟的一套互联网 ...
- Django REST framework 源码剖析
前言 Django REST framework is a powerful and flexible toolkit for building Web APIs. 本文由浅入深的引入Django R ...
- 跨站请求伪造(csrf),django的settings源码剖析,django的auth模块
目录 一.跨站请求伪造(csrf) 1. 什么是csrf 2. 钓鱼网站原理 3. 如何解决csrf (1)思路: (2)实现方法 (3)实现的具体代码 3. csrf相关的装饰器 (1)csrf_p ...
随机推荐
- JavaSE——打印流
打印流: 类PrintStream: PrintStream 为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式.它还提供其他两项功能.与其他输出流不同,PrintStream 永远不会抛 ...
- OSGI企业应用开发(十三)OSGI Web应用开发(二)
上篇文章介绍了OSGI Web应用的两种开发模式,并把Jetty应用服务器以Bundle的形式整合到Equinox容器中,已这种模式开发Web应用,所有的应用程序资源,例如Servlet.JSP.HT ...
- ActiveReports 报表中 RDF 文件解析
AcitveReport 提供两种报表模板,XML(RPX)模板和Code-Based 模板.两者都可以用于设计报表. 但是 RDF 格式文件我们大多不是很了解.许多客户在提到 RDF 格式时显得无所 ...
- wampserver的配置教程
对于初做PHP网站的朋友来说,第一步肯定是希望在自己电脑是搭建PHP环境,省去空间和上传的麻烦!但搭建环境也不是件容易的事情,特别是对于新手同学来说!因此在这里跟大家介绍我作为一名新手在使用的方便好用 ...
- XQuery使用sum求和,提示char不能转换为money解决方法
select axml.value('sum(/root/pro/price)','money') 以上代码提示‘char不能转换为money’的错误,发现值为'0.0E0'.改为: select a ...
- 数据库小组第N次小组会议
时间:5.30晚,9:30 ~ 11:30 主题:讨论android app与服务器之间数据同步的技术选型与实现 与会人:陈兆庭,黄志鹏,吴雪晴 讨论内容: 大体分析 关于数据同步,整体上有两部分,用 ...
- leveldb源码分析--BloomFilter
bloomfilter是leveldb中的一大性能利器,所以为了文章的表现完整性这里新启这么一篇文章.leveldb中的bloomfilter的实现在bloom.cc中,是一个较为简单的实现,所以就不 ...
- SQL Server 2012 列存储索引分析(转载)
一.概述 列存储索引是SQL Server 2012中为提高数据查询的性能而引入的一个新特性,顾名思义,数据以列的方式存储在页中,不同于聚集索引.非聚集索引及堆表等以行为单位的方式存储.因为它并不要求 ...
- IE 出现stack overflow 报错的原因归纳
1. 重定义了系统的触发事件名称作为自定义函数名如: onclick / onsubmit ... 都是系统保留的事件名称,不允许作为重定义函数名称: 2. IE缓存满了,无法写入.解决办法:清空 ...
- JDK5 新特性之 可变参数的方法(2)---asList
> Arrays.asList(T - a)方法的使用 >UnsupportedOperationException分析 Arrays.asList(T - a)方法的使用 pac ...