restframework学习笔记——源码解读之解析器

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表单提交
<form method...>
	input...
</form>
ajax提交
$.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...}
})

restframewoek

发起请求,经过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

你可能感兴趣的:(Django学习笔记)