小谈DRF之解析器相关
5. 解析器 *
5.1 基于Django如何解析数据
5.1.1 request.POST中如何才能取到值?
要求:
- 请求头的要求:
- Content-Type : application/x-www-form-urlencoded
- PS : 如果请求头中的Content-Type : application/x-www-form-urlencoded,request.POST中才有值,也就是说才会去request.body中去解析数据;
- 数据格式的要求:
- name=alex&age=18&gender=男
5.1.2 查看源码
先看我们写的代码:
# app01.urls.py
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
# url(r'^admin/', admin.site.urls),
url(r'^(?P<version>[v1|v2]+)/users/$', views.UsersView.as_view(), name='uuu'),
url(r'^(?P<version>[v1|v2]+)/django/$', views.DjangoView.as_view(), name='ddd'),
]
# views.py
class DjangoView(APIView):
def post(self, request, *args, **kwargs):
from django.core.handlers.wsgi import WSGIRequest
print(type(request._request))
return HttpResponse('request.body和request.POST')
看如下图示:
显示查看Django原生request(request._request)的类型,然后查看其(WSGIRequest)源码:
找到他的GET,查看其源码:
在GET的下面找到POST,查看_get_post
的源码:
在POST中找到_load_post_and_files()方法,查看其源码:
5.1.3 request.body和request.POST中都有值的情况
情况一:form表单提交数据时,Django内部会自动转换数据并将Content-Type转换为application/x-www-form-urlencoded,所以request.body和request.POST中都有值。
<form method...>
input...
</form>
情况二:ajax提交数据时,也是这样:Django内部会自动转换数据并将Content-Type转换为application/x-www-form-urlencoded,所以request.body和request.POST中都有值。
$.ajax({
url:...
type:POST,
data:{name:alex, age:18} # 内部转化为:name=alex&age=18
})
5.1.4 request.body有值,但是request.POST中没有值的情况
情况一:数据格式对,但是Content-Type的类型不对;
$.ajax({
url:...
type:POST,
headers:{'Content-Type': 'application/json'}
data:{name:alex, age:18} # 内部转化为:name=alex&age=18
})
情况二:数据格式和Content-Type的类型都不对;
$.ajax({
url:...
type:POST,
headers:{'Content-Type': 'application/json'}
data:JSON.stringfy({name:alex, age:18}) # json数据:{name:alex,age:18}
})
# 如果要拿到数据,必须进行json.loads(request.body)
5.1.5 关于Django能否正常解析数据
因为Django内部只支持Content-Type:application/x-www-form-urlencoded
并且数据类型为name=alex&age=18
,所以必须要满足这两点,Django才能正常解析数据。
5.2 rest framework的解析器
rest framework的解析器就是对请求体的数据进行解析。
5.2.1 简单示例
from rest_framework.parsers import JSONParser
class ParserView(APIView):
parser_classes = [JSONParser,]
"""
JSONParser:表示只能解析Content-Type:application/json头
"""
def post(self, request, *args, **kwargs):
"""
允许用户发送JSON数据:
1. Content-Type:application/json
2. {"name":"alex","age":18}
"""
print(request.data)
return HttpResponse('parser')
利用postman进行测试:
查看控制台结果:
因为JSONParser只能解析Content-Type:application/json头
,所以此时如果在postman中将Content-Type改为x-www-form-urlencoded
的话,会报错,如图示:
此时如果还想支持Content-Type:x-www-form-urlencoded
的情况,需要修改视图代码,将FormParser也放在parser_classes中即可:
from rest_framework.parsers import JSONParser, FormParser
class ParserView(APIView):
parser_classes = [JSONParser, FormParser]
"""
JSONParser:表示只能解析Content-Type:application/json头
FormParser:表示只能解析Content-Type:x-www-form-urlencoded头
"""
def post(self, request, *args, **kwargs):
"""
允许用户发送JSON数据:
1. Content-Type:application/json
2. {"name":"alex","age":18}
:param request:
:param args:
:param kwargs:
:return:
"""
print(request.data)
return HttpResponse('parser')
此时再用postman进行测试,如图所示:
此时控制台显示结果也是正确的:
最后说一下代码中的request.data
:
- 如果没有这行代码的话,解析器工作了吗?
- 没有工作,如果没有这行代码,我们只是构造了解析器但是没有使用;
- 执行这行代码都有哪些步骤:
- 获取用户的请求头;
- 获取用户的请求体;
- 根据用户的请求头和
parser_classes = [JSONParser, FormParser]
中的请求头对应; parser_classes = [JSONParser, FormParser]
中哪个合适,就用哪个类的对象去用户的请求体中解析数据;- 将解析后的数据赋值给
request.data
;
5.2.2 源码分析
还是先从dispatch()方法入手:
继续查看initialize_request()方法的源码:
继续查看get_parsers()方法的源码:
继续查看parser_classes的源码:
将解析器的类封装到了Request后,就一直没有处理他。
直到我们需要获取request.data时才会触发解析器相关的一系列操作,所以我们从request.data的源码开始看,到底触发了那些操作。
查看request.data的源码:
继续查看_load_data_and_files()方法的源码:
继续查看self._parse()方法的源码:
随便查看一个parser_classes中的一个类的源码就会有parse()方法,查看parse()方法的源码:
5.2.3 总结
5.2.3.1 解析器的使用
解析器一般也是全局设置即可,如果有特殊需求的视图,可以单独添加parser_classes即可。
全局设置如下:
# settings.py
REST_FRAMEWORK = {
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
'DEFAULT_VERSION': 'v2', # 默认版本
'ALLOWED_VERSIONS': ['v1', 'v2'], # 允许的版本
'VERSION_PARAM': 'version', # 在浏览器中?version中的version,可以修改
# 解析器的全局配置
'DEFAULT_PARSER_CLASSES': ['rest_framework.parsers.JSONParser', 'rest_framework.parsers.FormParser']
}
5.2.3.2 解析器的本质
解析器就是对用户请求体中的数据进行解析,依靠请求头中的Content-Type对请求体中的数据进行解析,解析到request.data中(由request.data触发)。