【DRF-08】rest-framework之解析器
-
1.解析器的作用
- 根据请求头 content-type 选择对应的解析器对请求体内容进行处理。
- 有application/json,x-www-form-urlencoded,form-data等格式, 可以自己自行配置支持或者不支持哪种格式, 一般在实际的生产环境中用json一种数据格式进行数据交互就够了, 如果需要form-data来进行文件处理,再将该格式加入到解析器中即可
-
2.全局使用解析器
# setting.py
REST_FRAMEWORK = {
"DEFAULT_VERSION":"v1",
"ALLOWED_VERSIONS":["v1","v2"],
"VERSION_PARAM":"version",
"DEFAULT_PARSER_CLASSES":[
"rest_framework.parsers.JSONParser",
"rest_framework.parsers.FormParser",
]
}
# 视图
class ParserView(APIView):
# parser_classes = [FileUploadParser]
def post(self,request,*args,**kwargs):
print(request.content_type)
"""
1. 获取用户请求
2. 获取用户请求体
3. 根据用户请求头 和 parser_classes = [JSONParser,FormParser,] 中支持的请求头进行比较
4. JSONParser对象去请求体
5. request.data
"""
print(request.data)
return HttpResponse("解析器")
-
3.不同解析器与传参的对应关系
-
4.解析器源码流程
- 4.1:首先还是走dispatch-->>initialize_request方法中,request中封装了解析器对象,从全局配置中获取
def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
parser_context = self.get_parser_context(request)
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(), # [Foo(),Bar()]
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
def get_parsers(self):
"""
Instantiates and returns the list of parsers that this view can use.
"""
return [parser() for parser in self.parser_classes]
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
。。。。
- 4.2:当视图中调用request.data时,执行request中的data方法
@property
def data(self):
if not _hasattr(self, '_full_data'):
self._load_data_and_files()
return self._full_data
- 4.3:执行_load_data_and_files方法,调用self._parse()
def _load_data_and_files(self):
"""
Parses the request content into `self.data`.
"""
if not _hasattr(self, '_data'):
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
- 4.4:执行self._parse,调用parser = self.negotiator.select_parser(self, self.parsers)
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)
# self.parsers(全局配置中的解析器)
# self 请求对象self.content_type
parser = self.negotiator.select_parser(self, self.parsers)
if not parser:
raise exceptions.UnsupportedMediaType(media_type)
try:
parsed = parser.parse(stream, media_type, self.parser_context)
except Exception:
# If we get an exception during parsing, fill in empty data and
# re-raise. Ensures we don't simply repeat the error when
# attempting to render the browsable renderer response, or when
# logging the request or similar.
self._data = QueryDict('', encoding=self._request._encoding)
self._files = MultiValueDict()
self._full_data = self._data
raise
# Parser classes may return the raw data, or a
# DataAndFiles object. Unpack the result as required.
try:
return (parsed.data, parsed.files)
except AttributeError:
empty_files = MultiValueDict()
return (parsed, empty_files)
- 4.5:执行select_parser,根据每个解析器的media_type与请求的content_type比较,返回对应的parser
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:
if media_type_matches(parser.media_type, request.content_type):
return parser
return None
- 4.6:执行4.4中parsed = parser.parse(stream, media_type, self.parser_context)方法。不同解析器的不同parser方法从而进行解析,如JSONParser
def parse(self, stream, media_type=None, parser_context=None):
"""
Parses the incoming bytestream as JSON and returns the resulting data.
"""
parser_context = parser_context or {}
encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
try:
decoded_stream = codecs.getreader(encoding)(stream)
parse_constant = json.strict_constant if self.strict else None
return json.load(decoded_stream, parse_constant=parse_constant)
except ValueError as exc:
raise ParseError('JSON parse error - %s' % str(exc))