Django源码分析 request.POST 与 request.body 区别
request.POST
request实际上是django/core/handlers/wsgi.py::WSGIRequest
的实例,而WSGIRequest
是HttpRequest
的子类
class WSGIRequest(http.HttpRequest):
def _get_post(self):
if not hasattr(self, '_post'):
self._load_post_and_files()
return self._post
def _set_post(self, post):
self._post = post
POST = property(_get_post, _set_post)
获取request.POST
的时候实际上是调用了WSGIRequest._get_post()
方法
现在来看一下_load_post_and_files()
方法
def _load_post_and_files(self):
"""Populate self._post and self._files if the content-type is a form type"""
if self.method != 'POST':
self._post, self._files = QueryDict(encoding=self._encoding), MultiValueDict()
return
if self._read_started and not hasattr(self, '_body'):
self._mark_post_parse_error()
return
if self.content_type == 'multipart/form-data':
if hasattr(self, '_body'):
# Use already read data
data = BytesIO(self._body)
else:
data = self
try:
self._post, self._files = self.parse_file_upload(self.META, data)
except MultiPartParserError:
self._mark_post_parse_error()
raise
elif self.content_type == 'application/x-www-form-urlencoded':
self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict()
else:
self._post, self._files = QueryDict(encoding=self._encoding), MultiValueDict()
正如方法的说明所述,这个方法只会处理content-type为表单类型的请求数据
因此当Content-Type=application/json
时,我们取request.POST
只会得到一个空的QueryDict
request.body
class HttpRequest(object):
@property
def body(self):
if not hasattr(self, '_body'):
# 调用read()之后会将此参数置为True
if self._read_started:
raise RawPostDataException("You cannot access body after reading from request's data stream")
# Limit the maximum request data size that will be handled in-memory.
# 通过请求头的CONTENT_LENGTH参数得到请求的数据大小,单位为字节(byte)。但有时候获取不到,见
# https://www.cnblogs.com/nxlhero/p/11670942.html
if (settings.DATA_UPLOAD_MAX_MEMORY_SIZE is not None and
int(self.META.get('CONTENT_LENGTH') or 0) > settings.DATA_UPLOAD_MAX_MEMORY_SIZE):
raise RequestDataTooBig('Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.')
try:
self._body = self.read()
except IOError as e:
six.reraise(UnreadablePostError, UnreadablePostError(*e.args), sys.exc_info()[2])
# 由于request.POST和request.body都会去读self._stream,因此需要在用完之后复原
self._stream = BytesIO(self._body)
return self._body
def read(self, *args, **kwargs):
self._read_started = True
try:
# _stream将在WSGIRequest中被初始化为LimitedStream的实例
# self._stream = LimitedStream(self.environ['wsgi.input'], content_length)
# 即直接从WSGI server获取数据
return self._stream.read(*args, **kwargs)
except IOError as e:
six.reraise(UnreadablePostError, UnreadablePostError(*e.args), sys.exc_info()[2])
因此,request.body
取出来是字节码,需要将其编码为字符串
当Content-Type=application/json
时,还需要用json.loads
处理得到的json字符串
request_params = json.loads(request.body.decode("utf-8"))
统一获取请求数据
if request.method.lower() == "post":
# 当 Content-Type 为表单类型时,这里就可以直接获取到数据了
request_params = request.POST
if not request_params:
try:
# Content-Type=application/json 的情况
request_params = json.loads(request.body.decode("utf-8"))
except Exception:
# 声明了 Content-Type=application/json,却不传递json字符串
raise ApiException