为什么要有解析器?原因很简单,当后台和前端进行交互的时候数据类型不一定都是表单数据或者json,当然也有其他类型的数据格式,比如xml,所以需要解析这类数据格式就需要用到解析器(也可以将请求体拿到,然后利用其他模块进行解析)。
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
DEFAULTS = {
# Base API policies
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
),
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',#json解析器
'rest_framework.parsers.FormParser',#form解析器
'rest_framework.parsers.MultiPartParser'#二进制解析器
),#rest_framework 默认支持三种解析器
json解析器
from rest_framework.versioning import URLPathVersioning
from rest_framework.parsers import JSONParser
class UserView(APIView):
'''查看用户信息'''
parser_classes = [JSONParser,]
versioning_class =URLPathVersioning
def get(self,request,*args,**kwargs):
res={"name":"wd","age":22}
return JsonResponse(res,safe=True)
def post(self,request,*args,**kwargs):
print(request.data) #获取解析后的请求结果
return JsonResponse({"success":"ok"}, safe=True)
使用postman向http://127.0.0.1:8000/login视图发送json数据,注意请求头必须是application/json,如下图:

from rest_framework.parsers import JSONParser
class LoginViewSet(APIView):
parser_classes = [JSONParser, ]
authentication_classes = []
def post(self,request):
name = request.data.get('name')
pwd = request.data.get('pwd')
user = User.objects.filter(name=name,pwd=pwd).first()
res = {"state_code":1000,"msg":None}
if user:
token =get_random_str(user.name)
Token.objects.update_or_create(user=user,defaults={"token":token})
res['token'] = token
# return Response('ok')
else:
res["state_code"] = 1001
res["msg"] = "用户名或密码错误"
import json
return Response(res)
form表单解析器
from rest_framework.versioning import URLPathVersioning
from rest_framework.parsers import JSONParser,FormParser
class UserView(APIView):
'''查看用户信息'''
parser_classes = [JSONParser,FormParser]
##JSONParser,解析头信息Content-Type:application/json,的json数据
##FormParser,解析头信息Content-Type:x-www-form-urlencoded数据
versioning_class =URLPathVersioning
def get(self,request,*args,**kwargs):
res={"name":"wd","age":22}
return JsonResponse(res,safe=True)
def post(self,request,*args,**kwargs):
print(request.data) #获取解析后的请求结果
return JsonResponse({"success":"ok"}, safe=True)
使用postman发送form表单数据
源码剖析
根据以上示例,梳理解析器解析数据流程
- 获取用户请求
- 获取用户请求体
- 根据用户请求头信息和parase_classes=[...],中的请求头进行比较,匹配上请求头就使用该解析器处理
- 解析器从请求体中拿数据进行处理,处理完成之后将结果返回给request.data
同样和权限源码流程一样,请求进来,先执行APIView的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进行加工,丰富了一些功能
#Request(
# request,
# parsers=self.get_parsers(),
# authenticators=self.get_authenticators(),
# negotiator=self.get_content_negotiator(),
# parser_context=parser_context
# )
#request(原始request,[BasicAuthentications对象,])
#获取原生request,request._request
#获取认证类的对象,request.authticators
#1.封装request
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()方法,在该方法中,get_parsers用于获取解析器,并被封装到request.parsers中。
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(), #获取所有的解析器,封装到request.parsers中
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
get_parsers()源码,和认证、权限一样,解析器采用列表生成式返回解析器对象的列表,所以示例中定义解析器的变量是parser_classes:
def get_parsers(self):
"""
Instantiates and returns the list of parsers that this view can use.
"""
return [parser() for parser in self.parser_classes]#列表生成式,返回解析器对象
self.praser_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#默认解析器配置
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
# Allow dependency injection of other settings to make testing easier.
settings = api_settings
schema = DefaultSchema()
#rest_framework默认全局配置DEFAULTS = {
# Base API policies
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
),
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication'
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny',
),
'DEFAULT_THROTTLE_CLASSES': (),
'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'rest_framework.negotiation.DefaultContentNegotiation',
'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata',
'DEFAULT_VERSIONING_CLASS': None,
# Generic view behavior
'DEFAULT_PAGINATION_CLASS': None,
'DEFAULT_FILTER_BACKENDS': (),
# Schema
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema',
# Throttling
'DEFAULT_THROTTLE_RATES': {
'user': None,
'anon': None,
},
'NUM_PROXIES': None,
# Pagination
'PAGE_SIZE': None,
# Filtering
'SEARCH_PARAM': 'search',
'ORDERING_PARAM': 'ordering',
# Versioning
'DEFAULT_VERSION': None,
'ALLOWED_VERSIONS': None,
'VERSION_PARAM': 'version',
# Authentication
'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
'UNAUTHENTICATED_TOKEN': None,
# View configuration
'VIEW_NAME_FUNCTION': 'rest_framework.views.get_view_name',
'VIEW_DESCRIPTION_FUNCTION': 'rest_framework.views.get_view_description',
# Exception handling
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
'NON_FIELD_ERRORS_KEY': 'non_field_errors',
# Testing
'TEST_REQUEST_RENDERER_CLASSES': (
'rest_framework.renderers.MultiPartRenderer',
'rest_framework.renderers.JSONRenderer'
),
'TEST_REQUEST_DEFAULT_FORMAT': 'multipart',
# Hyperlink settings
'URL_FORMAT_OVERRIDE': 'format',
'FORMAT_SUFFIX_KWARG': 'format',
'URL_FIELD_NAME': 'url',
# Input and output formats
'DATE_FORMAT': ISO_8601,
'DATE_INPUT_FORMATS': (ISO_8601,),
'DATETIME_FORMAT': ISO_8601,
'DATETIME_INPUT_FORMATS': (ISO_8601,),
'TIME_FORMAT': ISO_8601,
'TIME_INPUT_FORMATS': (ISO_8601,),
# Encoding
'UNICODE_JSON': True,
'COMPACT_JSON': True,
'STRICT_JSON': True,
'COERCE_DECIMAL_TO_STRING': True,
'UPLOADED_FILES_USE_URL': True,
# Browseable API
'HTML_SELECT_CUTOFF': 1000,
'HTML_SELECT_CUTOFF_TEXT': "More than {count} items...",
# Schemas
'SCHEMA_COERCE_PATH_PK': True,
'SCHEMA_COERCE_METHOD_NAMES': {
'retrieve': 'read',
'destroy': 'delete'
},
}
#APISettings源码解析
def perform_import(val, setting_name):
"""
If the given setting is a string import notation,
then perform the necessary import or imports.
"""
if val is None:
return None
elif isinstance(val, six.string_types):
return import_from_string(val, setting_name)
elif isinstance(val, (list, tuple)):
return [import_from_string(item, setting_name) for item in val]
return val
def import_from_string(val, setting_name):
"""
Attempt to import a class from a string representation.
"""
try:
# Nod to tastypie's use of importlib.
module_path, class_name = val.rsplit('.', 1)
module = import_module(module_path)
return getattr(module, class_name)
except (ImportError, AttributeError) as e:
msg = "Could not import '%s' for API setting '%s'. %s: %s." % (val, setting_name, e.__class__.__name__, e)
raise ImportError(msg)
class APISettings(object):
"""
A settings object, that allows API settings to be accessed as properties.
For example:
from rest_framework.settings import api_settings
print(api_settings.DEFAULT_RENDERER_CLASSES)
Any setting with string import paths will be automatically resolved
and return the class, rather than the string literal.
"""
def __init__(self, user_settings=None, defaults=None, import_strings=None):
if user_settings:
self._user_settings = self.__check_user_settings(user_settings)
self.defaults = defaults or DEFAULTS
self.import_strings = import_strings or IMPORT_STRINGS
self._cached_attrs = set()
@property
def user_settings(self):
if not hasattr(self, '_user_settings'):
self._user_settings = getattr(settings, 'REST_FRAMEWORK', {})
return self._user_settings
def __getattr__(self, attr):
if attr not in self.defaults:
raise AttributeError("Invalid API setting: '%s'" % attr)
try:
# Check if present in user settings
val = self.user_settings[attr]
except KeyError:
# Fall back to defaults
val = self.defaults[attr]
# Coerce import strings into classes
if attr in self.import_strings:
val = perform_import(val, attr)
# Cache the result
self._cached_attrs.add(attr)
setattr(self, attr, val)
return val
def __check_user_settings(self, user_settings):
SETTINGS_DOC = "https://www.django-rest-framework.org/api-guide/settings/"
for setting in REMOVED_SETTINGS:
if setting in user_settings:
raise RuntimeError("The '%s' setting has been removed. Please refer to '%s' for available settings." % (setting, SETTINGS_DOC))
return user_settings
def reload(self):
for attr in self._cached_attrs:
delattr(self, attr)
self._cached_attrs.clear()
if hasattr(self, '_user_settings'):
delattr(self, '_user_settings')
api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)#生成类对象,user_setting没有传值.
def reload_api_settings(*args, **kwargs):
setting = kwargs['setting']
if setting == 'REST_FRAMEWORK':
api_settings.reload()
setting_changed.connect(reload_api_settings)
源码解析思路通认证一样只是填写参数不一样.
当调用request.data获取请求数据时候将使用解析器,下面是request.data源码:
@property#装饰器将方法变成实例对象.函数名的形式
def data(self):
if not _hasattr(self, '_full_data'):
self._load_data_and_files()
return self._full_data
#Request类属性源码展示
class Request(object):
"""
Wrapper allowing to enhance a standard `HttpRequest` instance.
Kwargs:
- request(HttpRequest). The original request instance.
- parsers_classes(list/tuple). The parsers to use for parsing the
request content.
- authentication_classes(list/tuple). The authentications used to try
authenticating the request's user.
"""
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
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 = {}
self.parser_context['request'] = self
self.parser_context['encoding'] = request.encoding or settings.DEFAULT_CHARSET
因为_full_data为空所以执行self._load_data_and_files(),获取请求数据或者文件数据,self._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() #执行self_parse(),获取解析器,并对content_type进行解析,选择解析器,返回数据
if self._files: #判断文件流数据,存在则加入到self._full_data(也就是我们的request.data)中
self._full_data = self._data.copy() ,
self._full_data.update(self._files)
else:
self._full_data = self._data #不存在将无文件流的解析完成的数据赋值到self._full_data(request.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
执行self._prase()方法,获取解析器,并对请求的Content-Type进行解析,选择解析器,返回解析后的数据,以下是self._prase源码:
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 #获取请求体中的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) #执行解析器的parse方法(从这里可以看出每个解析器都必须有该方法),对请求数据进行解析
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) #返回解析结果,元祖,解析后的数据在parsed.data(在load_data_and_files中使用self._data和self._files进行接受),
文件数据在parsed.files中
except AttributeError:
empty_files = MultiValueDict()
return (parsed, empty_files)
django rest framework 解析器源码,下面我们来看看示例中json解析器的源码:
class JSONParser(BaseParser):
"""
Parses JSON-serialized data.
"""
media_type = 'application/json' #解析的Content-Type类型
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) #本质使用json类进行解析
except ValueError as exc:
raise ParseError('JSON parse error - %s' % six.text_type(exc))
django rest framework解析本质是根据请求头中的Content-Type来实现,不同的类型使用不同的解析器,一个视图可有多个解析器。
#全局使用
REST_FRAMEWORK = {
#解析器
"DEFAULT_PARSER_CLASSES":["rest_framework.parsers.JSONParser","rest_framework.parsers.FormParser"]
}
#单一视图使用
parser_classes = [JSONParser,FormParser]
来源:https://www.cnblogs.com/Dxd-python/p/10723755.html