解析器
基本使用
from rest_framework.parsers import JSONParser,FormParser
from rest_framework.negotiation import DefaultContentNegotiation
class parser(APIView):
parser_classes = [JSONParser,FormParser]#所有的解析器
content_negotiation_class = DefaultContentNegotiation#根据请求,匹配对应的解析器
def post(self,request):
#请求体中的参数会解析并整合到request.data
print(request.data)
# self.dispatch()
return Response({'cie':21})
源码流程
## 6 ##
class Request:
def __init__(self, request, parsers=None, authenticators=None,
negotiator=None, parser_context=None):
assert isinstance(request, HttpRequest), (
'The `request` argument must be an instance of '
'`django.http.HttpRequest`, not `{}.{}`.'
.format(request.__class__.__module__, request.__class__.__name__)
)
self._request = request
## 6.1 ## 将 parsers,negotiator赋值给self.parsers,self.negotiator
self.parsers = parsers or () #如果没有值则为空元组
self.authenticators = authenticators or ()
self.negotiator = negotiator or self._default_negotiator()
self.parser_context = parser_context
self._data = Empty
self._files = Empty
self._full_data = Empty
self._content_type = Empty
self._stream = Empty
if self.parser_context is None:
self.parser_context = {}
## 6.2 ## parser_context又新建了两个键:现在的self.parser_context--> {视图对象,URL路由参数,drf的request,encoding}
self.parser_context['request'] = self
self.parser_context['encoding'] = request.encoding or settings.DEFAULT_CHARSET
class APIView():
def perform_content_negotiation(self, request, force=False):
## 7.1 ##
renderers = self.get_renderers() #渲染器,视图以什么方式展示,与解析器无关,先跳过
conneg = self.get_content_negotiator()
try:
return conneg.select_renderer(request, renderers, self.format_kwarg)
except Exception:
if force:
return (renderers[0], renderers[0].media_type)
raise
## 7 ##
def initial(self, request, *args, **kwargs):
self.format_kwarg = self.get_format_suffix(**kwargs)
## 7.1 ## 渲染器self.perform_content_negotiation
neg = self.perform_content_negotiation(request)
request.accepted_renderer, request.accepted_media_type = neg
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
def get_content_negotiator(self):
##5.3##实例化self.content_negotiation_class()
if not getattr(self, '_negotiator', None):
self._negotiator = self.content_negotiation_class()
return self._negotiator
def get_parsers(self):
## 5.2 ##循环获取并实例化self.parser_classes中的对象
return [parser() for parser in self.parser_classes]
def get_parser_context(self, http_request):
## 3 ##返回的是一个字典
return {
'view': self, #当前的对象
'args': getattr(self, 'args', ()), #dispatch中的self.args = args
'kwargs': getattr(self, 'kwargs', {}) #dispatch中的self.kwargs = kwargs
}
def initialize_request(self, request, *args, **kwargs):
##2## 调用self.get_parser_context,返回值是视图对象,URL路由参数
parser_context = self.get_parser_context(request)
## 4 ##实例化一个Request
return Request(
request, #django的request
## 5.1 ##读取所有的解析器的对象
parsers=self.get_parsers(), #解析器的对象 [JSONParser,FormParser]
authenticators=self.get_authenticators(), #认证组件,形式是[对象1,对象2,..]
negotiator=self.get_content_negotiator(), #返回DefaultContentNegotiation()
parser_context=parser_context #上方的字典{视图对象,URL路由参数}
)
def dispatch(self, request, *args, **kwargs):
self.args = args
self.kwargs = kwargs
##1## 在initialize_request中对请求进行处理
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
## 7 ##
self.initial(request, *args, **kwargs)
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中只是帮我们实例化了解析器,那什么时候才会解析请求体中的内容呢?
当我们调用request.data时就会进行解析。又假如我们调用两次request.data那又会解析几次呢?
来看看源码怎么说的
## 4 ## JSONParser解析器
class JSONParser(BaseParser):
media_type = 'application/json'
renderer_class = renderers.JSONRenderer
strict = api_settings.STRICT_JSON
def parse(self, stream, media_type=None, parser_context=None):
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) #josn.load
except ValueError as exc:
raise ParseError('JSON parse error - %s' % str(exc))
## 3 ## 寻找解析器
class DefaultContentNegotiation(BaseContentNegotiation):
settings = api_settings
def select_parser(self, request, parsers): #循环所有的解析器对象
for parser in parsers: #匹配content_type和media_type
if media_type_matches(parser.media_type, request.content_type):
return parser #返回匹配成功的解析器
return None
class Response():
def _parse(self):
## 2 ## 解析流程
media_type = self.content_type #content_type就是请求里的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
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)
## 3 ## 获取解析器,negotiator就是DefaultContentNegotiation(),会寻找每一个解析器的media_type与请求的content_type对比
parser = self.negotiator.select_parser(self, self.parsers)
if not parser: #如果没有支持请求的类型的解析器,抛出异常
raise exceptions.UnsupportedMediaType(media_type)
try: #有匹配的解析器,执行parser.parse()
parsed = parser.parse(stream, media_type, self.parser_context)
## 4 ##JSONParser解析器->json.load不是loads,因为请求的数据不是字符串,loads是处理字符串的,请求的二进制数据存储在内存中,可以read()读取,相当于一个文件对象,使用load()
#FormParser解析器—>解析 name=asd&password=213 解析成QueryDict(django提供的)
except Exception:
self._data = QueryDict('', encoding=self._request._encoding)
self._files = MultiValueDict()
self._full_data = self._data
raise
try: #返回解析器的成员变量data和files
return (parsed.data, parsed.files)
except AttributeError:
empty_files = MultiValueDict()
return (parsed, empty_files)
def _load_data_and_files(self):
## 1.1 ##
if not _hasattr(self, '_data'): #有无self._data
self._data, self._files = self._parse() ## 2 ## 将_data解析
if self._files:
self._full_data = self._data.copy() #解析过一次后,第二次解析将直接返回self._full_data
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
@property
def data(self):
if not _hasattr(self, '_full_data'): #反射执行,self._full_data
## 1 ##
self._load_data_and_files() #如果没有则self._load_data_and_files()
return self._full_data #有则返回解析后的数据
@property
def content_type(self):
#请求头信息
meta = self._request.META
return meta.get('CONTENT_TYPE', meta.get('HTTP_CONTENT_TYPE', ''))
文件的上传
默认的解析器
print(self.parser_classes)
#[<class 'rest_framework.parsers.JSONParser'>, <class 'rest_framework.parsers.FormParser'>, <class 'rest_framework.parsers.MultiPartParser'>]
#MultiPartParser 既可以上传文件又能上传json
修改全局配置
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES':['rest_framework.parsers.JSONParser','rest_framework.parsers.FormParser',]
}