手撸drf中Request对象的data属性实现原理
django基础中提交post请求有两种方式:form表单;ajax提交数据。其中:form表单可以提交的数据可是有两种:urlencoded和form-data;ajax可以提交的数据有三种,除了urlencoded和form-data,还支持json格式的数据。
我们清楚的知道urlencoded格式的数据会被django封装到request.POST
字典中,文件数据会被django封装到request.FILES
中;而json数据比较特殊,django没有帮我们封装,而是原封不动的保存在request.body
中,需要我们手动从请求体中自己反序列化取出。
django的drf框架,帮我们优化了这个取数据的方式,统一一个取数据的接口Request.data
,即不论是json数据还是文件数据还是普通的在request.POST
的数据,都统一从request.data
中取出。
其实原理很简单,我们在中间件中,将数据统一取出并处理然后赋值给request的自定义data属性
import json
from django.utils.deprecation import MiddlewareMixin
class MyMiddleware(MiddlewareMixin):
def process_request(self, request):
# print(request)
if not request.POST:
if request.is_ajax():
request.data = json.loads(request.body)
else:
request.data = dict(request.POST) # 因为QueryDict是不可变字典,另一种解决方式是使用QueryDict的内置copy方法,实现深拷贝并且拷贝后的字典可以修改。
request.data.update(request.FILES) # 将文件数据也放在request.data中
补充:另一种解决方式,异常捕获
import json
from django.utils.deprecation import MiddlewareMixin
class JsonMiddel(MiddlewareMixin):
def process_request(self, request):
try:
request.data=json.loads(request.body)
except Exception as e:
request.data=request.POST
注意点:
- form表单和ajax同时提交的bug:将form标签中input的submit类型和button按钮绑定了ajax事件。这样的结果是触发了两次提交请求:form表单的和ajax的请求。避免的办法是不要给这两个标签绑定ajax请求,或者使用input的submit类型绑定ajax,则不会触发表单的数据提交。
- QueryDict是一个特殊的字典,它内部继承dict,但QueryDict是一个不可变字典无法修改。如果需要修改它可以使用其内置的copy()方法。
- CommonMiddleware中间件控制了是否重定向到带/的地址
def should_redirect_with_slash(self, request):
"""
Return True if settings.APPEND_SLASH is True and appending a slash to
the request path turns an invalid path into a valid one.
"""
if settings.APPEND_SLASH and not request.path_info.endswith('/'):
urlconf = getattr(request, 'urlconf', None)
return (
not is_valid_path(request.path_info, urlconf) and
is_valid_path('%s/' % request.path_info, urlconf)
)
return False