• drf(六)—解析器


    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]
    

    image-20220408133626809

    数据一般是封装在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都比较常用。

    image-20220408135909716

    使用时直接使用全局配置进行配置。

    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']
    }
    

    image-20220408164651432

    继续努力,终成大器。

  • 相关阅读:
    CentOS 7 iSCSI 多路径Multipath配置
    Centos7 GRE Tunnel
    ESXi开启虚拟化
    ownCloud 10.2.1搭建云盘服务器
    cinder不同类型volume转换
    linux的bond及子接口配置
    openstack 平台添加vGPU
    ownCloud 10.2.1搭建云盘服务器
    openstack nova 热迁移问题
    bugku-web40对git命令的使用
  • 原文地址:https://www.cnblogs.com/Blogwj123/p/16118183.html
Copyright © 2020-2023  润新知