RestFramework之解析器源码

一,restframework中的解析器

  • 在restframework中,默认可以解析JSONParser, FormParser, MultiPartParser(json数据, urlencode数据, 二进制文件类型数据(图片,视频等))这三种数据格式;
  • RestFramework中,当我们使用CBV写视图函数,继承RestFramework中的APIView时,只要我们在自己写的CBV类中,定义了  parser_classes = [ ]这个属性,那么restframework就会根据我们写在这      个属性中的值,而解析相对应的数据格式;
  • 这个parser_classes = [ ] 属性可以选择的值有三个  JSONParser, FormParser, MultiPartParser,即restframework给我们提供的三种可以解析的数据格式;

二,源码解析

首先,写一个django_demo:

views.py:

from django.http import JsonResponse
from app01 import models
from rest_framework.views import APIView
from rest_framework.parsers import JSONParser, FormParser, MultiPartParser

class Book(APIView):
    parser_classes = [FormParser]

    def get(self, request):
        book_list = models.Book.objects.all()
        temp = []
        for book in book_list:
            temp.append({"title": book.title, "publish_date": book.publishDate})
        return JsonResponse(temp, safe=False)

    def post(self, request):
        print("post")
        print(request.data)
        return HttpResponse("ok")

当我们使用postman发送post请求,数据时json格式是,会抛出:"Unsupported media type \"application/json\" in request."这样的错误,显然是不支持解析json数据格式的数据,但是我们的post方法中打印的post却打印了,说明问题出在了request.data上面; 也就是说在这里进行了格式解析验证。

看看执行流程:

首先,我们继承APIView类时,

url(r"^book", views.Book.as_view())

这里的as._view(),是APIView中的方法,(APIView继承的django的View类):

@classmethod
    def as_view(cls, **initkwargs):
      ‘‘‘ ... ‘‘‘
        view = super(APIView, cls).as_view(**initkwargs)  # 调用的原生的View中的as_view()方法
        view.cls = cls
        view.initkwargs = initkwargs

        # Note: session based authentication is explicitly CSRF validated,
        # all other authentication is CSRF exempt.
        return csrf_exempt(view)   # 这里return的时原生的View中的返回值

一旦有用户发送请求时,view函数就会执行:

        def view(request, *args, **kwargs):
            """ ... """
       self = cls(**initkwargs)  # 这里的self就是我们自己写的那个CBV类的实例化的self
            return self.dispatch(request, *args, **kwargs)

执行self.dispatch()方法,先从我们的CBV类中找,接着在APIView中找,发现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
        # 重新封装了django的request对象
        request = self.initialize_request(request, *args, **kwargs)
         # 把新的request对象重新赋给了原来的request
        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  # 重新封装的响应对象

重点来了,解析器是怎么解析的?我们去看一下这句话   request = self.initialize_request(request, *args, **kwargs)

在这个方法中:self.initialize_request():

    def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
        parser_context = self.get_parser_context(request)

        return Request(    # restframework自己的Request类实例化得到的新的 request对象
            request,  # 这个request就是传进来的那个旧的request对象
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )

应为之前我们就是卡在了request.data那里, 接下来去看看 Request类中的data方法:

@property   # 是一个静态方法
    def data(self):
        if not _hasattr(self, ‘_full_data‘):
            self._load_data_and_files()
        return self._full_data

再看   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()   # 关心这句话
            if self._files:
                self._full_data = self._data.copy()
                self._full_data.update(self._files)
            else:
                self._full_data = self._data

看看  self._data, self._files = self._parse():

 def _parse(self):
        """
        Parse the request content, returning a two-tuple of (data, files)

        May raise an `UnsupportedMediaType`, or `ParseError` exception.
        """

     """ ...... """
        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) #这里就是实际在解析数据了,这个不用关心(无非就是针对不同的数据格式,使用相对应的格式去解析数据,封装到data中)
        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)

再看是如何选择解析器的: self.negotiator.select_parser(self, self.parsers):

注意这个方法中传递两个参数,self ,就是我们那个新的request对象(在Request()那里得到的),第二个参数就是self.parsers,即实例属性,也是在Request()实例化的时候传递的参数;

    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:  # 实际上是在循环我们传进来的那个self.parsers,
            if media_type_matches(parser.media_type, request.content_type):
                return parser
        return None

可以看到该方法中,是在循环那个self.parsers,也就是说,restframework可以解析那种数据格式,取决那个新的request对象实例化的时候(Request())中那个参数self.parsers:

这样我们就需要去看看 Request() 实例化的时候到底是怎么传递参数的:

    def initialize_request(self, request, *args, **kwargs):  # 这里的self是我们的CBV类的实例化对象,一步步传递过来的
        """
        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
        )

在 self.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]  # 看到的就是我们定义的  parser_classes这个属性, 优先在我们的CBV类中查找

 那么如果 我们有自己定义 parser_classes, 就会去循环我们写的那个属性的列表,也就实现了我们自己自定义可以解析的的数据格式;(前提是我们写的解析器是restframework中能解析的)。

但是!但是!但是! 如果我们没有自己定义 parser_classes, 就会去父类中找,也就是APIView中,在APIView中:

parser_classes = api_settings.DEFAULT_PARSER_CLASSES  # 默认的parser_classes

直接使用api_settings . 这个DEFAULT_PARSER_CLASSES, 我们去看看api_settings:

api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)  #是APISettings的实例化对象

而我们发现在 APISettings这个类中就没有  DEFAULT_PARSER_CLASSES这个属性,也就是说用 api_settings.DEFAULT_PARSER_CLASSES 拿不到属性,那么就会执行 __getattr__()方法:

    def __getattr__(self, attr):  # 这里的attr就是 DEFAULT_PARSER_CLASSES
        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]  # 这里先去 用户自己的settings中去拿那么属性,如果能拿到就使用用户自己settings中的那个默认值
        except KeyError:
            # Fall back to defaults
            val = self.defaults[attr]  # 在用户的settings中拿不到,再去拿默认的配置的 属性;

        # 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

settings中的defaults是一个大字典:

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‘
    ),
    """ ... 等等许多默认值 """ 

    },
}

通过这个方法,可以获得用户的settings中的配置:

@property
    def user_settings(self):
        if not hasattr(self, ‘_user_settings‘):
            self._user_settings = getattr(settings, ‘REST_FRAMEWORK‘, {})
        return self._user_settings

也就是说我们可以在settings中自己配置一个字典叫  REST_FRAMEWORK={},如:

REST_FRAMEWORK = {
    ‘DEFAULT_PARSER_CLASSES‘: (
        # ‘rest_framework.parsers.JSONParser‘,
        ‘rest_framework.parsers.FormParser‘,
        ‘rest_framework.parsers.MultiPartParser‘
    ),
}

这么配置的话,我们的解析器就不能解析json格式的数据了。

over!

原文地址:https://www.cnblogs.com/glh-ty/p/9670339.html

时间: 2024-08-30 02:02:48

RestFramework之解析器源码的相关文章

Python解析器源码加密系列之(二):一次使用标准c的FILE*访问内存块的尝试

摘要:由于近期打算修改Python解释器以实现pyc文件的加密/解密,出于保密的要求,解密之后的数据只能放在内存中,不能写入到文件中.但是后续的解析pyc文件的代码又只能接受FILE*作为入参,所以就提出了一种把通过FILE*来访问内存的需求,下文是针对这个需求的几个方面的尝试及其结论. 以下尝试的前提是:Win7 + VS2010. 在vc中,FILE其实就是_iobuf,定义如下: struct _iobuf { char *_ptr; //文件输入的下一个位置 int _cnt; //当前

<JavaScript语言精粹>JSON解析器源码阅读

1 // 这是一个用JavaScript编写JSON解析器的实现方案: 2 var jsonParser = (function() { 3 // 这是一个能把JSON文本解析成JavaScript数据结构的函数. 4 // 它是一个简单的递归降序解析器. 5 // 我们在另一个函数中定义此函数,以避免创建全局变量. 6 7 var at, // 当前字符索引 8 ch, // 当前字符 9 escapee = { 10 '"': '"', 11 "\\": &qu

美女图片采集器 (源码+解析)

前言: 有一段时间没写博客了, "持之以恒"徽章都暗了, 实在不该. 前一段确实比较忙, ...小小地给自己的懒找个借口吧. 大二即将结束, 学习iOS也有一段时间了.今天抽点时间, 开源一个前几天刚上传的App里面的一个功能, RT, 美女图片采集器.   美女.. 相信没有人不喜欢吧, 基于此, 这个小Demo应运而生. 效果演示: 看到这里, 如果还有兴趣学习的话, 可以先到我的git中下载源码, 然后配合着源码看我下面的解析.相信, 会让你有所收获的. git下载链接: Bea

Android短信插入器源码

package com.examp.createsms; import android.app.Activity; import android.content.ContentResolver; import android.content.ContentValues; import android.net.Uri; import android.os.Bundle; public class MainActivity extends Activity { @Override protected

自制Android中的短息备份器源码

短信的实体bean package com.examp.backsms.domain; public class SmsInfo { private String address; private String date; private String type; private String body; public SmsInfo() { } public SmsInfo(String address, String date, String type, String body) { thi

C语言解析JSON源码

2020-01-09 关键字:cJSON.linux JSON解析 JSON 是一种在互联网领域内很常用的轻量级数据交换协议. 它与 XML 的地位差不多,但就笔者而言,笔者更喜欢 JSON 的风格,因为它更符合我们的思维习惯,同样一份数据,JSON 格式的就是比 XML 要清晰明了一些. 最近笔者需要在 C语言 上解析 JSON 格式,在网上一顿找,找到一份很不错的开源代码.经过一阵研究与修改以后,终于变成了让笔者用的很顺手的 C语言 版 JSON 解析器. 现将这份经笔者小小修改过的代码记录

GlusterFS源码解析 —— GlusterFS 源码安装

安装环境: CentOS6.2 glusterfs-3.4.3 GlusterFS 挂载需要 fuse 支持,如果你的内核版本低于 2.6.16 则需要下载fuse的源码包自行编译安装,也可下载 fuse 的rpm包.安装fuse的方法我就不说了,不会源码安装的直接去rpmfind.net上下载rpm即可.高于此版本的内核中已经有了fuse.ko的模块,需要的时候可以执行以下命令进行加载: modprobe -b fuse 1.下载GlusterFS的源码包,目前已经有更新版本 : wget h

C++、VC++、MFC网页自动注册、登陆、发帖、留言,QQ注册、QQ申请器源码、注册邮箱源码、自动发帖源码

C++.VC++.MFC网页自动注册.登陆.发帖.留言,QQ注册.QQ申请器源码.注册邮箱源码.自动发帖源码 参考资料: 自动登录yahoo邮箱http://blog.csdn.net/suisuibianbian/archive/2005/12/12/550260.aspx VC采集网页所有表单域http://blog.csdn.net/fjssharpsword/archive/2010/12/17/6081689.aspx 说说这类软件最常见的使用方式吧. 也许你经常看到有人发布了以下这类

Android中图片加载框架Glide解析2----从源码的角度理解Glide的执行流程

转载地址:http://blog.csdn.net/guolin_blog/article/details/53939176 在本系列的上一篇文章中,我们学习了Glide的基本用法,体验了这个图片加载框架的强大功能,以及它非常简便的API.还没有看过上一篇文章的朋友,建议先去阅读 Android图片加载框架最全解析(一),Glide的基本用法 . 在多数情况下,我们想要在界面上加载并展示一张图片只需要一行代码就能实现,如下所示: Glide.with(this).load(url).into(i