Django Rest Framework源码剖析(五)-----解析器
<table style="height: 30px; background-color: #afeeee; width: 1266px; ; width: 1266px;" border="0"> |
同样以订单视图为例,添加json解析器,如下:
</span><span style="color: #0000ff;">def</span> get(self,request,*args,**<span style="color: #000000;">kwargs):
res</span>={<span style="color: #800000;">"</span><span style="color: #800000;">name</span><span style="color: #800000;">"</span>:<span style="color: #800000;">"</span><span style="color: #800000;">wd</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">age</span><span style="color: #800000;">"</span>:22<span style="color: #000000;">}
</span><span style="color: #0000ff;">return</span> JsonResponse(res,safe=<span style="color: #000000;">True)
</span><span style="color: #0000ff;">def</span> post(self,**<span style="color: #000000;">kwargs):
</span><span style="color: #0000ff;">print</span>(request.data) <span style="color: #008000;">#</span><span style="color: #008000;">获取解析后的请求结果</span>
<span style="color: #0000ff;">return</span> JsonResponse({<span style="color: #800000;">"</span><span style="color: #800000;">success</span><span style="color: #800000;">"</span>:<span style="color: #800000;">"</span><span style="color: #800000;">ok</span><span style="color: #800000;">"</span>},safe=True)</pre>
使用postman向http://127.0.0.1:8000/api/v1/user视图发送json数据,注意请求头必须是application/json,如下图:
查看post结果(结果直接是json格式):
2.form表单解析器
视图
</span><span style="color: #0000ff;">def</span> get(self,safe=<span style="color: #000000;">True)
</span><span style="color: #0000ff;">def</span> post(self,safe=True)</pre>
使用postman发送form表单数据
后台接受,并且结果已经转化为QueryDict类型了
<table style="height: 30px; background-color: #afeeee; width: 1266px; ; width: 1266px;" border="0">
<tr><td><span style="font-size: 16px;">三、源码剖析</td>
</tr></table>
1.根据以上示例,梳理解析器解析数据流程
- 获取用户请求
- 获取用户请求体
- 根据用户请求头信息和parase_classes=[...],中的请求头进行比较,匹配上请求头就使用该解析器处理
- 解析器从请求体中拿数据进行处理,处理完成之后将结果返回给request.data
2.源码剖析:
同样和一样,请求进来,先执行APIView的dispatch方法,以下是源码,分析请看注解
dispatch方法:
<span style="color: #0000ff;">try</span><span style="color: #000000;">:
self.initial(request,</span>*args,**<span style="color: #000000;">kwargs)
</span><span style="color: #008000;">#</span><span style="color: #008000;"> Get the appropriate handler method</span>
<span style="color: #0000ff;">if</span> request.method.lower() <span style="color: #0000ff;">in</span><span style="color: #000000;"> self.http_method_names:
handler </span>=<span style="color: #000000;"> getattr(self,request.method.lower(),self.http_method_not_allowed)
</span><span style="color: #0000ff;">else</span><span style="color: #000000;">:
handler </span>=<span style="color: #000000;"> self.http_method_not_allowed
response </span>= handler(request,**<span style="color: #000000;">kwargs)
</span><span style="color: #0000ff;">except</span><span style="color: #000000;"> Exception as exc:
response </span>=<span style="color: #000000;"> self.handle_exception(exc)
self.response </span>= self.finalize_response(request,response,**<span style="color: #000000;">kwargs)
</span><span style="color: #0000ff;">return</span> self.response</pre>
执行initialize_request()方法,在该方法中,get_parsers用于获取解析器,并被封装到request.parsers中。
get_parsers()源码,和认证、权限一样,解析器采用列表生成式返回解析器对象的列表,所以示例中定义解析器的变量是parser_classes:
self.praser_classes,默认(全局)配置
</span><span style="color: #008000;">#</span><span style="color: #008000;"> The following policies may be set at either globally,or per-view.</span>
renderer_classes =<span style="color: #000000;"> api_settings.DEFAULT_RENDERER_CLASSES
<span style="color: #ff6600;"> parser_classes
<span style="color: #ff6600;">=<span style="color: #000000;"><span style="color: #ff6600;"> api_settings.DEFAULT_PARSER_CLASSES #解析器全局配置authentication_classes =<span style="color: #000000;"> api_settings.DEFAULT_AUTHENTICATION_CLASSES
throttle_classes =<span style="color: #000000;"> api_settings.DEFAULT_THROTTLE_CLASSES
permission_classes =<span style="color: #000000;"> api_settings.DEFAULT_PERMISSION_CLASSES
content_negotiation_class =<span style="color: #000000;"> api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
metadata_class =<span style="color: #000000;"> api_settings.DEFAULT_METADATA_CLASS
versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
当调用request.data获取请求数据时候将使用解析器,下面是request.data源码:
执行self._load_data_and_files(),获取请求数据或者文件数据,self._load_data_and_files()源码:
</span></span><span style="color: #008000;">#</span><span style="color: #008000;"> if a form media type,copy data & files refs to the underlying</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> http request so that closable objects are handled appropriately.</span>
<span style="color: #0000ff;">if</span><span style="color: #000000;"> is_form_media_type(self.content_type):
self._request._post </span>=<span style="color: #000000;"> self.POST
self._request._files </span>= self.FILES</pre>
执行self._prase()方法,获取解析器,并对请求的Content-Type进行解析,选择解析器,返回解析后的数据,以下是self._prase源码:
May raise an `UnsupportedMediaType`,or `ParseError` exception.
</span><span style="color: #800000;">"""</span><span style="color: #000000;">
media_type </span>=<span style="color: #000000;"> self.content_type <span style="color: #ff6600;">#获取请求体中的Content-Type
</span></span><span style="color: #0000ff;">try</span><span style="color: #000000;">:
stream </span>=<span style="color: #000000;"> self.stream <span style="color: #ff6600;">#如果是文件数据,则获取文件流数据
</span></span><span style="color: #0000ff;">except</span><span style="color: #000000;"> RawPostDataException:
</span><span style="color: #0000ff;">if</span> <span style="color: #0000ff;">not</span> hasattr(self._request,<span style="color: #800000;">'</span><span style="color: #800000;">_post</span><span style="color: #800000;">'</span><span style="color: #000000;">):
</span><span style="color: #0000ff;">raise</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> If request.POST has been accessed in middleware,and a method='POST'</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> request was made with 'multipart/form-data',then the request stream</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> will already have been exhausted.</span>
<span style="color: #0000ff;">if</span><span style="color: #000000;"> self._supports_form_parsing():
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> (self._request.POST,self._request.FILES<span style="color: #ff6600;">) #处理文件类型数据</span>
stream </span>=<span style="color: #000000;"> None
</span><span style="color: #0000ff;">if</span> stream <span style="color: #0000ff;">is</span> None <span style="color: #0000ff;">or</span> media_type <span style="color: #0000ff;">is</span><span style="color: #000000;"> None:
</span><span style="color: #0000ff;">if</span> media_type <span style="color: #0000ff;">and</span><span style="color: #000000;"> is_form_media_type(media_type):
empty_data </span>= QueryDict(<span style="color: #800000;">''</span>,encoding=<span style="color: #000000;">self._request._encoding)
</span><span style="color: #0000ff;">else</span><span style="color: #000000;">:
empty_data </span>=<span style="color: #000000;"> {}
empty_files </span>=<span style="color: #000000;"> MultiValueDict()
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> (empty_data,empty_files)
parser </span>=<span style="color: #000000;"> self.negotiator.select_parser(self,self.parsers) <span style="color: #ff6600;">#选择解析器,
</span></span><span style="color: #0000ff;">if</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> parser:
</span><span style="color: #0000ff;">raise</span><span style="color: #000000;"> exceptions.UnsupportedMediaType(media_type)
</span><span style="color: #0000ff;">try</span><span style="color: #000000;">:
parsed </span>=<span style="color: #000000;"> parser.parse(stream,media_type,self.parser_context) <span style="color: #ff6600;">#执行解析器的parse方法(从这里可以看出每个解析器都必须有该方法),对请求数据进行解析
</span></span><span style="color: #0000ff;">except</span><span style="color: #000000;"> Exception:
</span><span style="color: #008000;">#</span><span style="color: #008000;"> If we get an exception during parsing,fill in empty data and</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> re-raise. Ensures we don't simply repeat the error when</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> attempting to render the browsable renderer response,or when</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> logging the request or similar.</span>
self._data = QueryDict(<span style="color: #800000;">''</span>,encoding=<span style="color: #000000;">self._request._encoding)
self._files </span>=<span style="color: #000000;"> MultiValueDict()
self._full_data </span>=<span style="color: #000000;"> self._data
</span><span style="color: #0000ff;">raise</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> Parser classes may return the raw data,or a</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> DataAndFiles object. Unpack the result as required.</span>
<span style="color: #0000ff;">try</span><span style="color: #000000;">:
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> (parsed.data,parsed.files) <span style="color: #ff6600;"> #返回解析结果,元祖,解析后的数据在parsed.data(在load_data_and_files中使用self._data和self._files进行接受), 文件数据在parsed.files中
</span></span><span style="color: #0000ff;">except</span><span style="color: #000000;"> AttributeError:
empty_files </span>=<span style="color: #000000;"> MultiValueDict()
</span><span style="color: #0000ff;">return</span> (parsed,empty_files)</pre>
以上就是整个django rest framework 解析器源码,下面我们来看看示例中json解析器的源码,说明请看注解:
</span><span style="color: #0000ff;">def</span> parse(self,stream,media_type=None,parser_context=<span style="color: #000000;">None): <span style="color: #ff6600;">#在源码中解读过,该方法用于解析请求体
</span></span><span style="color: #800000;">"""</span><span style="color: #800000;">
Parses the incoming bytestream as JSON and returns the resulting data.
</span><span style="color: #800000;">"""</span><span style="color: #000000;">
parser_context </span>= parser_context <span style="color: #0000ff;">or</span><span style="color: #000000;"> {}
encoding </span>= parser_context.get(<span style="color: #800000;">'</span><span style="color: #800000;">encoding</span><span style="color: #800000;">'</span><span style="color: #000000;">,settings.DEFAULT_CHARSET)
</span><span style="color: #0000ff;">try</span><span style="color: #000000;">:
decoded_stream </span>=<span style="color: #000000;"> codecs.getreader(encoding)(stream)
parse_constant </span>= json.strict_constant <span style="color: #0000ff;">if</span> self.strict <span style="color: #0000ff;">else</span><span style="color: #000000;"> None
</span><span style="color: #0000ff;">return</span> json.load(decoded_stream,parse_constant=<span style="color: #000000;">parse_constant) <span style="color: #ff6600;">#本质使用json类进行解析
</span></span><span style="color: #0000ff;">except</span><span style="color: #000000;"> ValueError as exc:
</span><span style="color: #0000ff;">raise</span> ParseError(<span style="color: #800000;">'</span><span style="color: #800000;">JSON parse error - %s</span><span style="color: #800000;">'</span> % six.text_type(exc))</pre>
django rest framework解析本质是根据请求头中的Content-Type来实现,不同的类型使用不同的解析器,一个视图可有多个解析器。
2.使用:
</span><span style="color: #008000;">#</span><span style="color: #008000;">解析器</span>
<span style="color: #800000;">"</span><span style="color: #800000;">DEFAULT_PARSER_CLASSES</span><span style="color: #800000;">"</span>:[<span style="color: #800000;">"</span><span style="color: #800000;">rest_framework.parsers.JSONParser</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">rest_framework.parsers.FormParser</span><span style="color: #800000;">"</span><span style="color: #000000;">]
}
<span style="color: #008000;">#<span style="color: #008000;">单一视图使用parser_classes = [JSONParser,FormParser]
(编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!