drf(六)—解析器
问题引出:请求头一般都会有多种的形式。最常见的是json
形式,和form-data
。其中json是最常见的形式。
解析器,一般用来解析用户发送过来的数据;
1.请求头要求
# django:request.POST/ request.body
# 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=男
封装后数据封装在request.data
中。
1. 源码流程
入口为dispatch()
def dispatch(self, request, *args, **kwargs):
"""
`.dispatch()` is pretty much the same as Django's regular dispatch,
but with extra hooks for startup, finalize, and exception handling.
"""
self.args = args
self.kwargs = kwargs
# 封装request.data
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
self.initial(request, *args, **kwargs)
# Get the appropriate handler method
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
response = handler(request, *args, **kwargs)
except Exception as exc:
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
initialize_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(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
get_parsers 函数
def get_parsers(self):
"""
Instantiates and returns the list of parsers that this view can use.
"""
# 列表生成式,循环生成对象。
return [parser() for parser in self.parser_classes]
数据一般是封装在request对象中的data中。
# Request类中data的源码。
from rest_framework.request import Request
@property #装饰为属性
def data(self):
if not _hasattr(self, '_full_data'):
self._load_data_and_files()
return self._full_data
def _hasattr(obj, name):
# 使用反射获取属性名称,并判断值是否存在。
return not getattr(obj, name) is Empty
_load_data_and_files()函数
def _load_data_and_files(self):
"""
Parses the request content into `self.data`.
"""
if not _hasattr(self, '_data'):
self._data, self._files = self._parse() #执行 _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
_parse()函数
def _parse(self):
"""
Parse the request content, returning a two-tuple of (data, files)
May raise an `UnsupportedMediaType`, or `ParseError` exception.
"""
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)
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)
2.全局使用
解析器主要使用内置的类进行解析数据;
class BaseParser:
"""
All parsers should extend `BaseParser`, specifying a `media_type`
attribute, and overriding the `.parse()` method.
"""
media_type = None
def parse(self, stream, media_type=None, parser_context=None):
"""
Given a stream to read from, return the parsed representation.
Should return parsed data, or a `DataAndFiles` object consisting of the
parsed data and files.
"""
# 每个数据解析类必须要重写该方法。
raise NotImplementedError(".parse() must be overridden.")
class JSONParser(BaseParser):
"""
Parses JSON-serialized data.
"""
media_type = 'application/json'
renderer_class = renderers.JSONRenderer
strict = api_settings.STRICT_JSON
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))
class FormParser(BaseParser):
"""
Parser for form data.
"""
media_type = 'application/x-www-form-urlencoded'
def parse(self, stream, media_type=None, parser_context=None):
"""
Parses the incoming bytestream as a URL encoded form,
and returns the resulting QueryDict.
"""
parser_context = parser_context or {}
encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
return QueryDict(stream.read(), encoding=encoding)
主要在系统中运用后两个,form和json都比较常用。
使用时直接使用全局配置进行配置。
REST_FRAMEWORK={
"DEFAULT_AUTHENTICATION_CLASSES":['app01.utils.auth.MyAuthentication',],
"UNAUTHENTICATED_USER":None, # 匿名,request.user = None
"UNAUTHENTICATED_TOKEN":None,
"DEFAULT_PERMISSION_CLASSES":['app01.utils.permission.MyPermission',],
"DEFAULT_THROTTLE_CLASSES":['app01.utils.throttle.MyThrottle',],# 匿名用户不能在全局配置需要为登录功能单独添加
"DEFAULT_THROTTLE_RATES":{
"visit":'3/m',#一分钟三次,匿名用户
"loginuser":'10/m',# 登录成功,一分钟10次
},
"DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",
"DEFAULT_VERSION":'v1',
"ALLOWED_VERSIONS":['v1','v2'], #允许的版本号
"VERSION_PARAM":"version",# 这个参数应该和 路由中的名称相同version/
# 解析器的配置
"DEFAULT_PARSER_CLASSES":['rest_framework.parsers.JSONParser','rest_framework.parsers.FormParser']
}
继续努力,终成大器。