一、简介

解析器顾名思义就是对请求体进行解析。为什么要有解析器?原因很简单,当后台和前端进行交互的时候数据类型不一定都是表单数据或者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源码剖析(五)-----解析器的更多相关文章

  1. Django Rest Framework源码剖析(八)-----视图与路由

    一.简介 django rest framework 给我们带来了很多组件,除了认证.权限.序列化...其中一个重要组件就是视图,一般视图是和路由配合使用,这种方式给我们提供了更灵活的使用方法,对于使 ...

  2. Django Rest Framework源码剖析(三)-----频率控制

    一.简介 承接上篇文章Django Rest Framework源码剖析(二)-----权限,当服务的接口被频繁调用,导致资源紧张怎么办呢?当然或许有很多解决办法,比如:负载均衡.提高服务器配置.通过 ...

  3. Django Rest Framework源码剖析(四)-----API版本

    一.简介 在我们给外部提供的API中,可会存在多个版本,不同的版本可能对应的功能不同,所以这时候版本使用就显得尤为重要,django rest framework也为我们提供了多种版本使用方法. 二. ...

  4. Django Rest Framework源码剖析(七)-----分页

    一.简介 分页对于大多数网站来说是必不可少的,那你使用restful架构时候,你可以从后台获取数据,在前端利用利用框架或自定义分页,这是一种解决方案.当然django rest framework提供 ...

  5. Django Rest Framework源码剖析(六)-----序列化(serializers)

    一.简介 django rest framework 中的序列化组件,可以说是其核心组件,也是我们平时使用最多的组件,它不仅仅有序列化功能,更提供了数据验证的功能(与django中的form类似). ...

  6. Django Rest Framework源码剖析(二)-----权限

    一.简介 在上一篇博客中已经介绍了django rest framework 对于认证的源码流程,以及实现过程,当用户经过认证之后下一步就是涉及到权限的问题.比如订单的业务只能VIP才能查看,所以这时 ...

  7. Django Rest Framework源码剖析(一)-----认证

    一.简介 Django REST Framework(简称DRF),是一个用于构建Web API的强大且灵活的工具包. 先说说REST:REST是一种Web API设计标准,是目前比较成熟的一套互联网 ...

  8. Django REST framework 源码剖析

    前言 Django REST framework is a powerful and flexible toolkit for building Web APIs. 本文由浅入深的引入Django R ...

  9. 跨站请求伪造(csrf),django的settings源码剖析,django的auth模块

    目录 一.跨站请求伪造(csrf) 1. 什么是csrf 2. 钓鱼网站原理 3. 如何解决csrf (1)思路: (2)实现方法 (3)实现的具体代码 3. csrf相关的装饰器 (1)csrf_p ...

随机推荐

  1. Android网络编程系列之HTTP协议原理总结

    前言 作为搞移动开发的我们,免不了与网络交互打交道.虽然市面上很多开源库都封装的比较到位,我们实现网络访问也轻车熟路.但还是十分有必要简要了解一下其中的原理,以便做到得心应手,也是通往高级开发工程师甚 ...

  2. JAVA:函数的定义、使用

      本文内容: 什么是函数 函数的定义格式 函数的重载(overload): 函数的调用使用注意 关于形式参数的使用 首发时间:2017-06-23 修改时间:2018-03-21:修改了布局,修改了 ...

  3. Oracle EBS AP 应付核销到确定一行预付款

    -- purpose: 应付标准发票核销预付款发票中的一行 -- 12.2.6 环境 -- author:jenrry create_date: 2017-06-08 declare l_error_ ...

  4. Oracle EBS AP 取消发票

    --取消发票 created by jenrry 20170425 declare l_result BOOLEAN; l_message_name VARCHAR2(240); l_invoice_ ...

  5. 调用百度翻译API接口功能

    public string appid = "自己的APPID"; public string q = "要翻译的文本"; "; public str ...

  6. 根据IP查找计算机名

    查找10.10.10.X段的计算机名称 $r="10.10.10." $s=1..254 foreach ($a in $s) { $ip = $r + [string]$a $i ...

  7. <button>与<input type="button">

    在做form表单,点击按钮随机生成两串密钥的时候 1.用第一种按钮的时候,会出现刷新form表单的现象.会把创建密钥前面的输入框中的字消失.虽然能生成密钥1和密钥2,但是会闪一下,随即消失.几个输入框 ...

  8. pt-table-checksum工具MySQL主从复制数据一致性

    所使用的工具是pt-table-checksum 原理是: 在主上执行检查语句去检查 mysql主从复制的一致性,生成 replace 语句,然后通过复制传递到从库,再通过update 更新 mast ...

  9. apk静态注射[转]-未实践

    原文:http://free0coding.iteye.com/blog/1684263 1.将需要注入的代码块打包成jar1,释放一个公共类的静态方法a  2.反编译apk得到smali文件,在适当 ...

  10. 用JS制作《飞机大作战》游戏_第1讲(素材查找和界面框架搭建)-陈远波

    一.查找素材: 二.分析游戏界面框架: 登录界面.游戏界面.暂停游戏界面.玩家死亡后弹出界面:并对应的界面包含什么元素: 三.分别搭建以上四个界面: 1.登录界面与游戏界面框架(隐藏游戏界面,四个界面 ...