class APIView(View):
# The following policies may be set at either globally, or per-view.
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
# 解析器配置
parser_classes = api_settings.DEFAULT_PARSER_CLASSES
# 认证配置
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
# 节流限制访问配置
throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
# 权限配置
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
metadata_class = api_settings.DEFAULT_METADATA_CLASS
# 版本配置
versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
django:request.POST/ request.body
1. 请求头要求:
Content-Type: application/x-www-form-urlencoded
PS: 如果请求头中的 Content-Type: application/x-www-form-urlencoded,request.POST中才有值(去request.body中解析数据)。
2. 数据格式要求:
name=alex&age=18&gender=男
例如:
<form method...>
input...
</form>
$.ajax({
url:...
type:POST,
data:{name:alex,age=18} # 内部转化 name=alex&age=18&gender=男
})
情况一:
# body有值;POST无
$.ajax({
url:...
type:POST,
headers:{'Content-Type':"application/json"}
data:{name:alex,age=18} # 内部转化 name=alex&age=18&gender=男
})
情况二:
# body有值;POST无
# json.loads(request.body)
$.ajax({
url:...
type:POST,
headers:{'Content-Type':"application/json"}
data:JSON.stringfy({name:alex,age=18}) # {name:alex,age:18...}
})
发起请求,经过diapatch()中的:
request = self.initialize_request(request, *args, **kwargs)
initialize_request()中:
def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
parser_context = self.get_parser_context(request)
return Request(
request,
# 先获取解析器,把解析器实例化成对象封装到Request中
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
get_parsers()中返回的是解析器的列表生成式,列表中可以有多个解析器,
可以在配置文件中配置默认的解析器列表,也可以配置局部的解析器列表
def get_parsers(self):
return [parser() for parser in self.parser_classes]
'''
FormParser: media_type = 'application/x-www-form-urlencoded'
JSONParser: media_type = 'application/json'
MultiPartParser:media_type = 'multipart/form-data' form表单,可能包含文件
FileUploadParser: media_type = '*/*' 解析上传的文件
'''
from rest_framework.parsers import JSONParser, FormParser #常用的
class ParserView(APIView):
# parser_classes = [JSONParser,FormParser,]
"""
JSONParser:表示只能解析content-type:application/json头
JSONParser:表示只能解析content-type:application/x-www-form-urlencoded头
"""
def post(self,request,*args,**kwargs):
"""
允许用户发送JSON格式数据
a. content-type: application/json
b. {'name':'alex',age:18}
:param request:
:param args:
:param kwargs:
:return:
"""
"""
1. 获取用户请求
2. 获取用户请求体
3. 根据用户请求头 和 parser_classes = [JSONParser,FormParser,] 中支持的请求头进行比较
4. 如果是content-type: application/json,JSONParser对象去请求体
5. 通过request.data调用解析器去解析请求体
"""
print(request.data)
return HttpResponse('ParserView')
首先是Request()类中的data(),在data()中调用self._load_data_and_files()
@property
def data(self):
if not _hasattr(self, '_full_data'):
self._load_data_and_files()
return self._full_data
_load_data_and_files()中又调用_parse():
def _load_data_and_files(self):
"""
Parses the request content into `self.data`.
"""
if not _hasattr(self, '_data'):
#调用_parse()
self._data, self._files = self._parse()
if self._files:
self._full_data = self._data.copy()
self._full_data.update(self._files)
else:
self._full_data = self._data
# if a form media type, copy data & files refs to the underlying
# http request so that closable objects are handled appropriately.
if is_form_media_type(self.content_type):
self._request._post = self.POST
self._request._files = self.FILES
def _parse(self):
"""
Parse the request content, returning a two-tuple of (data, files)
May raise an `UnsupportedMediaType`, or `ParseError` exception.
"""
# 用户提交的请求头content_type的值
media_type = self.content_type
try:
stream = self.stream
except RawPostDataException:
if not hasattr(self._request, '_post'):
raise
# If request.POST has been accessed in middleware, and a method='POST'
# request was made with 'multipart/form-data', then the request stream
# will already have been exhausted.
if self._supports_form_parsing():
return (self._request.POST, self._request.FILES)
stream = None
if stream is None or media_type is None:
if media_type and is_form_media_type(media_type):
empty_data = QueryDict('', encoding=self._request._encoding)
else:
empty_data = {}
empty_files = MultiValueDict()
return (empty_data, empty_files)
'''
parsers:就是parser_classes = [JSONParser, FormParser]
self:请求对象 self.content_type
让它们进行比较,选择一个解析器并返回
'''
parser = self.negotiator.select_parser(self, self.parsers)
if not parser:
raise exceptions.UnsupportedMediaType(media_type)
try:
# 返回解析器后就执行该解析器的parse()方法
parsed = parser.parse(stream, media_type, self.parser_context)
except Exception:
self._data = QueryDict('', encoding=self._request._encoding)
self._files = MultiValueDict()
self._full_data = self._data
raise
...
找select_parser()的源码,以DefaultContentNegotiation()为例,其继承BaseContentNegotiation()。
class DefaultContentNegotiation(BaseContentNegotiation):
settings = api_settings
def select_parser(self, request, parsers):
"""
Given a list of parsers and a media type, return the appropriate
parser to handle the incoming request.
"""
for parser in parsers:
'''
循环每一个解析器,把解析器支持的media_type和请求体的content_type
进行对比,如果符合,就返回当前的parser
JSONParser:media_type = 'application/json'
FormParser:media_type = 'application/x-www-form-urlencoded'
'''
if media_type_matches(parser.media_type, request.content_type):
return parser
return None