drf之解析器


前言

解析器是干什么的?因为前后端分离,因为可能采用json、xml、html等各种不同格式的内容,后端必须要有一个解析器来解析前端发送过来的数据,也就是翻译器!否则后端凭什么看懂前端的数据?对应地,后端也有一个渲染器Render,和解析器是相反的方向,将后端的数据翻译成前端能明白的数据格式。

DRF框架提供了许多内置的Parser类,用来处理各种媒体类型的请求,比如json,比如xml。还支持自定义解析器,可以灵活地设计API接受的媒体类型。

Django原生的解析器对于post的数据,如果要从request.body中解析出来放到request.POST中,那么必须同时满足两个条件:

  • 请求头部 Content_type = ‘application/x-www-form-urlencoded’
  • 数据格式必须是: name=xxx&password=xxx&email=xxx…
    而对于前端发送过来的例如JSON格式的数据则无法处理(当然你自己处理也是可以的)。DRF则不同,它提供了一些额外的解析器帮我们处理各种格式。

DRF的parsers模块非常简单,只定义了几个解析器类:

  • BaseParser:解析器基类,以下四个类都直接继承它
  • JSONParser
  • FormParser
  • MultiPartParser
  • FileUploadParser

1、概述

将前端传入的数据,根据请求头,匹配相应的解析器JSONParser、FormParser、MultiPartParser、FileUploadParser,数据解析完成后赋值给request.data;

2、流程图

在这里插入图片描述

3、解析器

3.1 JSONParser

解析 JSON 格式的请求内容。其.media_type属性值为 application/json
在这里插入图片描述

3.2 FormParser

解析HTML表单内容,使用QueryDict的数据填充request.data。这也是Django原生支持的解析方式。

通常我们希望同时支持FormParser和MultiPartParser两种解析器,以便完全支持HTML表单数据。

.media_type: application/x-www-form-urlencoded

在这里插入图片描述

3.3 MultiPartParser

解析多部分的HTML表单内容,支持文件上传。

.media_type: multipart/form-data

在这里插入图片描述

3.4 FileUploadParser

解析原始文件上传内容。此时, request.data 属性将是一个字典,并且只包含一个键,这个键叫做 ‘file’ ,对应的值包含上传的文件内容。

如果使用FileUploadParser解析器的视图,在被调用的时候URL中携带一个 filename 关键字参数,则该参数将被用作文件名。如果在没有这个关键字参数的情况下调用它,则客户端必须在HTTP头部的 Content-Disposition 中设置文件名。

在这里插入图片描述

4、源码

4.1 视图类

class APIView(View):
	parser_classes = api_settings.DEFAULT_PARSER_CLASSES
    content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS


    def get_content_negotiator(self):

        if not getattr(self, '_negotiator', None):
            self._negotiator = self.content_negotiation_class()
        return self._negotiator


    def get_parsers(self):
        return [parser() for parser in self.parser_classes]

    def get_parser_context(self, http_request):
        return {
            'view': self,
            'args': getattr(self, 'args', ()),
            'kwargs': getattr(self, 'kwargs', {})
        }


    def initial(self, request, *args, **kwargs):

        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg

        self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request)

    def initialize_request(self, request, *args, **kwargs):

        parser_context = self.get_parser_context(request)	# 视图view,当前路由携带的参数

        return Request(
            request,									# django中的request
            parsers=self.get_parsers(),					# 示例话的解析器对象列表[MultiPartParser(), JSONParser(), FormParser()]
            authenticators=self.get_authenticators(),	# 认证
            negotiator=self.get_content_negotiator(),	# 实例化对象 ==>主要用于获取对应的解析器 DefaultContentNegotiation(BaseContentNegotiation)
            parser_context=get_parser_context			# 视图view,当前路由携带的参数,+ drf的reqeust + 编码格式
        )



    def dispatch(self, request, *args, **kwargs):

        self.args = args
        self.kwargs = kwargs
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request

        self.initial(request, *args, **kwargs)        
        response = handler(request, *args, **kwargs)

        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response



class HomeView(APIView):
    parser_classes = [JSONParser, FormParser, FileUploadParser]
    content_negotiation_class = DefaultContentNegotiation


    def post(self, request, *args, **kwargs):        
        print(request.content_type)
        print(request.data, type(request.data))

        return Response('ok')

4.2 Request

class Request:
	def __init__(self, request, parsers=None, authenticators=None,
                 negotiator=None, parser_context=None):

		self.parsers = parsers or ()
        self.negotiator = negotiator or self._default_negotiator()
        self.parser_context = parser_context

    	# 将drf的request封装进parser_context
        self.parser_context['request'] = self

        # 将编码格式封装进parser_context
        self.parser_context['encoding'] = request.encoding or settings.DEFAULT_CHARSET

    # 视图中执行request.data时会触发解析器对请求体中的数据进行解析
    @property
    def data(self):
        if not _hasattr(self, '_full_data'):
            self._load_data_and_files()
        return self._full_data



    def _parse(self):

        media_type = self.content_type
        stream = self.stream
        
        parser = self.negotiator.select_parser(self, self.parsers)

        if not parser:
            raise exceptions.UnsupportedMediaType(media_type)

        parsed = parser.parse(stream, media_type, self.parser_context)

        return (parsed.data, parsed.files)


    def _load_data_and_files(self):

        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 is_form_media_type(self.content_type):
                self._request._post = self.POST
                self._request._files = self.FILES

4.3 DefaultContentNegotiation

def media_type_matches(lhs, rhs):

    lhs = _MediaType(lhs)
    rhs = _MediaType(rhs)
    return lhs.match(rhs)

class DefaultContentNegotiation(BaseContentNegotiation):
    settings = api_settings

    def select_parser(self, request, parsers):
        for parser in parsers:
            if media_type_matches(parser.media_type, request.content_type):
                return parser
        return None

4.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)
        except ValueError as exc:
            raise ParseError('JSON parse error - %s' % str(exc))

5、总结

drf中默认的解析器为:JSONParser、FormParser、MultiPartParser

  • 26
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值