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