DRFのインストール構成 - ソースコード解析

DRFのインストール構成

DRFは何ですか

フロントエンドへのデータインタフェースは、主にフロントライトAPIに使用される別のサービス、の後端のために、DjangoのRESTフレームワークを提供略します。

なぜDRF必要があります

我々はDRFを適用していないにもかかわらず、として会うの仕様にRESTfulなインターフェースを作成することができますが、彼はすぐに私たちが対応インタフェースを設計するのに役立つことができたので、だけでなく、開発の効率を改善するためのツールとして、DRFを使用することもできますが、また、権限、認証およびその他の強力なを提供します機能。

DFFのインストールコマンド

cnpm install djangoframework

DRFの使用

インポートモジュールなので、そのクラスの継承APIView

class BookAPIView(APIView):
    # 渲染模块的局部配置
    # 局部禁用就是配置空list:[]
    renderer_classes = [JSONRenderer, BrowsableAPIRenderer]

    # 解析模块的局部配置
    # parser_classes = [JSONParser, MultiPartParser, FormParser]

    def get(self, request, *args, **kwargs):
        print(request._request.GET)
        print(request.GET)
        print(request.META)


        return Response({
            'status': 0,
            'msg': 'get ok'
        })

    def post(self, request, *args, **kwargs):
        # print(request._request.POST)
        # print(request.POST)
        # print(request.META.get('HTTP_AUTH'))


        # QueryDict转化为原生dict
        # print(request.query_params.dict())
        # print(type(request.data))
        # if isinstance(request.data, QueryDict):
        #     request.data.dict()


        print(request.data)

        print(a)

        return Response({
            'status': 0,
            'msg': 'post ok',
        })



ソースコード解析

DRF私たちは、ソースコードでAPIViewの外観を入力して、継承APIViewクラスにクラスを記述する必要があります。

私たちが電話をかけたいので方法は次の通りである(CBV)

from django.conf.urls import url

from . import views
urlpatterns = [
    url(r'^books/$', views.BookAPIView.as_view()),
]

ダイレクトコールas_view()ので、私たちはちょうどAPIViewのas_view方法を見てください。

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()

    @classmethod
    def as_view(cls, **initkwargs):
        """
        Store the original class on the view function.

        This allows us to discover information about the view when we do URL
        reverse lookups.  Used for breadcrumb generation.
        """
        # 这里判断 cls(我们写的继承了 APIView 的类)是否是queryset的子类,答案是“不是” ,所以这里就不用看他,直接看下面跳到下面的 2
        if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
            def force_evaluation():
                raise RuntimeError(
                    'Do not evaluate the `.queryset` attribute directly, '
                    'as the result will be cached and reused between requests. '
                    'Use `.all()` or call `.get_queryset()` instead.'
                )
            cls.queryset._fetch_all = force_evaluation
 # 2 这里调用了父类的 as_view方法。
        view = super().as_view(**initkwargs)
        view.cls = cls
        view.initkwargs = initkwargs

        # Note: session based authentication is explicitly CSRF validated,
        # all other authentication is CSRF exempt.
        return csrf_exempt(view)

書かれているビューAPIView継承された疑いの余地はありません。私たちは、DRF書き換えas_view DO、コメントから見ることができ、結果的に言うと、確かにオリジナルの方法にいくつかのアップグレードを行わないもの、as_viewの親クラスと呼ばれるas_view方法を見ることができます唯一のアップグレードは、「csrf_exempt(ビュー)」、そのビューはCSRFチェックを避けるためにすることを、このチェックCSRF見えるDRF感が有用ではないです。

親メソッドas_viewへ

その後、我々はこの方法を入力し、同じがないかを確認(実際には同じですが、同じではありません何かを見つけます。)。

    @classonlymethod
    def as_view(cls, **initkwargs):
        """
        Main entry point for a request-response process.
        """
        #这里对 initkwargs 做了一个遍历,但是没有传任何参数,所以直接跳过。
        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError("You tried to pass in the %s method name as a "
                                "keyword argument to %s(). Don't do that."
                                % (key, cls.__name__))
            if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r. as_view "
                                "only accepts arguments that are already "
                                "attributes of the class." % (cls.__name__, key))
        #这里是对 我们写的类的实例化对象进行赋值。
        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            self.request = request
            self.args = args
            self.kwargs = kwargs
            #最关键的一步就在这里。
            return self.dispatch(request, *args, **kwargs)
        view.view_class = cls
        view.view_initkwargs = initkwargs

        # take name and docstring from class
        update_wrapper(view, cls, updated=())

        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())
        return view

上記のコードでは、私はself.dispatch方法を示した最も重要なステップがあるコア部DRF。

彼は派遣のビューがありますので、発送はここで、中にCtrlキープラスポイントが残されてはならないが、実際には、我々は検索順序に従ってAPIViewにこの方法を持っている、つまり、自分自身を書きDRFは、まず私たち自身に行きますそこにクラスを書き、その後に親APIViewを探しに行くので、ここでは、見つけることがビューに行くAPIViewは派遣で発見されました。

APIViewの発送方法に

    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 = 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

コードのこの部分は、コアコードDRFです。

ビューのステップポイントバイステップでは、リクエストに応じてラベルで私を見ては、第二のパッケージ、ソースコードに見ていました。

要求モジュール速いです

self.initialize_request()メソッドを入力します。

この方法を調べるには、二次包装の要求を実現する方法です。

    def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
        # 这是解析模块,我们后面会讲。
        parser_context = self.get_parser_context(request)
        
        #这里返回了一个 Request类实例化的对象,把我们的request丢了进去。
        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )

ステップは、第二のパッケージを要求するのでなければならない必然行うための要求()で、彼を見て、オブジェクトがどのような種類のものであり、インスタンス化。

リクエスト()の中に、彼のinitメソッドを表示するには

    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__)
        )
        # 这里用 _request来保存了原生的request,完成了二次封装
        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

        force_user = getattr(request, '_force_auth_user', None)
        force_token = getattr(request, '_force_auth_token', None)
        if force_user is not None or force_token is not None:
            forced_auth = ForcedAuthentication(force_user, force_token)
            self.authenticators = (forced_auth,)

次に、あなたがしているか、どのようなrequest.b request.a何気なくポイントかどうか、それは要求がかかりますGETATTR方法はないですか?

彼の方法のGETATTRを見てください

 def __getattr__(self, attr):
        """
        If an attribute does not exist on this instance, then we also attempt
        to proxy it to the underlying HttpRequest object.
        """
        try:
            return getattr(self._request, attr)
        except AttributeError:
            return self.__getattribute__(attr)

彼はgetattribute__方法は、その性質に戻るには、簡単に言えば、Cで書かれている__、見つけて、それを見つけることができない、自分自身を見つけるために、中に入る_request FOUNDの内部を行くだろう。だから、次のような結論があります:

 drf的请求模块
1、drf的request是在wsgi的request基础上再次封装
2、wsgi的request作为drf的request一个属性:_request
3、新的request对旧的request做了完全兼容
4、新的request对数据解析更规范化:所有的拼接参数都解析到query_params中,所有数据包数据都被解析到data中
        query_params和data属于QueryDict类型,可以 .dict() 转化成原生dict类型

小概要:

""" 源码分析
1、drf的APIView类:重写了as_view(),但主体逻辑还是调用父类View的as_view(),局部禁用了csrf认证
    重点:所有继承drf的基本视图类APIView的视图类,都不在做csrf认证校验
2、drf的APIView类:重写了dispatch(),在内部对request进行了二次封装:self.initialize_request(request, *args, **kwargs)
    内部核心:
        走drf的Request初始化方法__init__:self._request = request
        drf的Request的getter方法__getattr__:先从self._request反射取属性,没取到再冲drf的request中取
"""
"""
核心:request除了可以访问原wsgi协议的request所有内容,还可以访问 query_params、data

これは、DRFモジュールはDRFレンダリングモジュールのプロセスについて話した後、要求を処理しています。

第二に、レンダリングモジュール

""" drf的渲染模块(了解)
这个模块就是决定最后你是可以用浏览器来访问还是json等等
1、可以在视图类中通过renderer_classes类属性对该视图的数据响应渲染做配置 - 局部配置
2、可以在项目的配置文件的drf配置中通过DEFAULT_RENDERER_CLASSES对该视图的数据响应渲染做配置 - 全局配置
注:如果一个视图类在有全局配置下,还进行了局部配置,优先走自己的局部配置
"""

ソース分析:

以前に最も重要な部分について話しましたが、派遣を書き換えることであるので、これは、見つけるために、ゼロからスタートする必要はありません。だから、派遣から直接見始めます。

 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 = 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了
            response = self.handle_exception(exc)
        # 这里对response进行了二次处理响应,内部完成了多种结果的渲染方式。
        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response

その結果、二次処理では、このアプローチに投げ込ま応答と要求して、そして再びました。

finalize_response()メソッドを入力します。

    def finalize_response(self, request, response, *args, **kwargs):
        """
        Returns the final response object.
        """
        # Make the error obvious if a proper response is not returned
        # 这是断言,判断你的response 是不是 HttpResponseBase的子类,断言通过的话就会继续往下走代码,没通过就会抛出异常,这里的意思就相当于是,如果你的视图函数没有返回 HttpResponse 类的对象,就会报错,也叫作响应基类。这里能通过,所以往下走。
        assert isinstance(response, HttpResponseBase), (
            'Expected a `Response`, `HttpResponse` or `HttpStreamingResponse` '
            'to be returned from the view, but received a `%s`'
            % type(response)
        )
        # 这里继续走,因为我们的response 是Response的子类。
        if isinstance(response, Response):
            # 第二个参数代表是允许的渲染类,去request里面拿,默认我们是没有写的,所以这里会进这个if。
            if not getattr(request, 'accepted_renderer', None):
                #内部解析了配置的渲染类
                neg = self.perform_content_negotiation(request, force=True)
                #这一步是解压赋值,就是解压缩,前面接受的是允许的渲染类,后面接受的是允许的渲染头的类型,所以说上一步的 neg 一定会被赋值一个元组,而且元组里面一定会有一个渲染类,进去看一下这个方法做了什么 ,accepted_media_type里面就是表示到底是渲染 json 还是 标签 还是页面
                request.accepted_renderer, request.accepted_media_type = neg
#  2        这里把一个一个东西都丢给了response,格式化他,然后要怎么渲染?这里是交给中间件来完成的,中间件会做这件事 response.accepted_renderer.render(response.renderer_context),来进行渲染,到此为止,再往下就会有很多很多东西,不用在看了。
            response.accepted_renderer = request.accepted_renderer
            response.accepted_media_type = request.accepted_media_type
            #这个就是他的内容,有view,有args,有kwargs,和request请求相对应的信息。
            response.renderer_context = self.get_renderer_context()

        # Add new vary headers to the response instead of overwriting.
        vary_headers = self.headers.pop('Vary', None)
        if vary_headers is not None:
            patch_vary_headers(response, cc_delim_re.split(vary_headers))

        for key, value in self.headers.items():
            response[key] = value

        return response

perform_content_negotiation()メソッドを入力します。

 def perform_content_negotiation(self, request, force=False):
        """
        Determine which renderer and media type to use render the response.
        """
        #进入这个方法看一下 renderers 是什么东西。
        renderers = self.get_renderers()
        conneg = self.get_content_negotiator()

        try:
            return conneg.select_renderer(request, renderers, self.format_kwarg)
        except Exception:
            if force:
                #这里确实返回了一个元组,就看看是什么东西吧,看上面
                return (renderers[0], renderers[0].media_type)
            raise

この方法は、get_renderersに入りました

 def get_renderers(self):
        """
        Instantiates and returns the list of renderers that this view can use.
        """
        return [renderer() for renderer in self.renderer_classes]

self.renderer_classesが中に入るからスカーレット・レターは、ビュークラスのビューにrenderer_classesクラスのプロパティで」***あなたは、私の内側に書き始めたことができる場所、えっ、一次微分のリストです構成データはレスポンスをレンダリングします - ローカル設定を「***

彼は、それを見つける自分自身を見つけるために開始する方法で、ここでの自己は、私たち自身のクラス自体を記述することであり、我々はそれを設定しない場合、それは中APIViewある親クラス、親クラス、に行きます。

# 第一行就是,父类也是先去自己的配置文件中找,配置文件又先走自己的配置文件,然后再去找默认的配置文件。
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

プロセスを検索:自分のビュークラスからAPIViewクラス属性(構成ファイルから見て)(ローカル設定)>>独自のプロジェクトプロファイル(グローバルコンフィギュレーション)> DRF疲れたプロファイルを

ただ、レンダラは、ブレースを追加背後に、これはこのクラスのレンダリングがあるの内側にこのリストが保存されている、オブジェクトのインスタンスは、オブジェクトをインスタンス化されている、リスト内包の背中を見てください。

2で見られるトップfinalize_response()メソッドに戻って続行

小概要:

""" 渲染模块源码分析
1、二次处理响应对象:APIView的dispatch方法 - self.finalize_response(request, response, *args, **kwargs)
2、获取渲染类对象:进入finalize_response方法 - self.perform_content_negotiation(request, force=True)
3、从配置文件中得到渲染类对象:perform_content_negotiation -> self.get_renderers() -> [renderer() for renderer in self.renderer_classes]
"""
"""
核心:可以全局和局部配置视图类支持的结果渲染:默认可以json和页面渲染,学习该模块的目的是开发可以全局只配置json方式渲染
"""

分析モジュール

""" drf的解析模块(了解) - 服务的对象是数据包数据
这个模块的作用是来解析你传来的数据的,因为有可能你传来的是 formdata类型,也有可能是json类型,它需要解析。
1、可以在视图类中通过parser_classes类属性对该视图的数据包解析做配置 - 局部配置
2、可以在项目的配置文件的drf配置中通过DEFAULT_PARSER_CLASSES对该视图的数据包解析做配置 - 全局配置
"""

どこにデータがそれを完了するために解析されますか?

 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和query_params里面,再次进入这个方法看一下
        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
    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
        )
    def get_parser_context(self, http_request):
        """
        Returns a dict that is passed through to Parser.parse(),
        as the `parser_context` keyword argument.
        """
        # Note: Additionally `request` and `encoding` will also be added
        #       to the context by the Request object.
        return {
            'view': self,
            'args': getattr(self, 'args', ()),
            'kwargs': getattr(self, 'kwargs', {})
        }

だから、これはメソッドが辞書を返すです。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]

だから、あなたはあなた自身の執筆クラスの内部に書くことができます

parser_classes =解析されたデータをJSONクラス、フォームデータ、URLエンコードを可能にする[JSONParser、MultiPartParser、FormParser]。

# JSONParser: json数据
# FormParser: urlencoded
# MultiPartParser:form-data

あなたはどのパーサで、それを見ることができる場所について

from rest_framework.parsers import JSONParser, FormParser, MultiPartParser
#drf默认配置了下面前三个
class JSONParser(BaseParser)
class FormParser(BaseParser)
class MultiPartParser(BaseParser)
class FileUploadParser(BaseParser)

同じ順序とレンダリングモジュールを検索します。彼らはまた、ファイル内部で独自の設定を行うことができます

REST_FRAMEWORK = {
    # 渲染模块的全局配置:开发一般只配置json
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
    ],
    # 解析模块的全局配置
    'DEFAULT_PARSER_CLASSES': [
        'rest_framework.parsers.JSONParser',
        'rest_framework.parsers.FormParser',
        'rest_framework.parsers.MultiPartParser'
    ],

    # 异常模块
    # 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
    'EXCEPTION_HANDLER': 'api.utils.exception_handler',
}

小概要

""" 解析模块源码分析
1、APIView的dispatch方法:self.initialize_request(request, *args, **kwargs)内部还提供了数据解析
2、self.get_parser_context(request)提供要解析的数据,self.get_parsers()提供解析的类对象(内部从配置中找解析类)
"""
"""
核心:请求的数据包格式会有三种(json、urlencoded、form-data),drf默认支持三种数据的解析,可以全局或局部配置视图类具体支持的解析方式
"""

モジュールおよび分析モジュールをレンダリングするように、最も重要なローカル設定とグローバル設定があります。これは、ライン上に設定されます。

異常モジュール

""" 异常模块(重点):重写异常模块目的是记录异常信息(项目上线)
1、在settings的drf配置中配置EXCEPTION_HANDLER,指向自定义的exception_handler函数
2、drf出现异常了,都会回调exception_handler函数,携带 异常对象和异常相关信息内容,
    在exception_handler函数完成异常信息的返回以及异常信息的logging日志
"""

ソースコードを解析し、その後、例外モデルを使用する方法についての話

Utilsのは、アプリケーション内のファイルを設定し、exception_handler.pyファイルを書き込みます。このファイルには、この機能を歩いているときに例外があり、exception_handle(EXC、コンテキスト)関数内に記述して、内部の設定を構成しています。

settings.py

REST_FRAMEWORK = {
    # 渲染模块的全局配置:开发一般只配置json
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
    ],
    # 解析模块的全局配置
    'DEFAULT_PARSER_CLASSES': [
        'rest_framework.parsers.JSONParser',
        'rest_framework.parsers.FormParser',
        'rest_framework.parsers.MultiPartParser'
    ],

    # 异常模块
    # 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
    'EXCEPTION_HANDLER': 'api.utils.exception_handler',
}

オリジナルバージョンexception_handle(EXC、コンテキスト)関数

from rest_framework.response import Response

def exception_handler(exc, context):
    # 开发阶段一定要记录日志
    # logging.error(exc)
    #因为只能返回字符串,所以要用这种形式来写
    return Response('%s - %s' % (context['view'].__class__.__name__, exc))

次のソースコード解析、我々は知っている、すべてのエントリが派遣されています

    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 = 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)
        #捕获到了一个异常,命名为 exc
        except Exception as exc:
            #然后调用这个方法,我们配置的叫做 exception_handle,所以对应的还不是我们写的那个。   这个方法就是异常模块
            response = self.handle_exception(exc)

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

その後、我々は見てhandle_exceptionは(EXC)メソッドを取ります

    def handle_exception(self, exc):
        """
        Handle any exception that occurs, by returning an appropriate response,
        or re-raising the error.
        """
        if isinstance(exc, (exceptions.NotAuthenticated,
                            exceptions.AuthenticationFailed)):
            # WWW-Authenticate header for 401 responses, else coerce to 403
            auth_header = self.get_authenticate_header(self.request)

            if auth_header:
                exc.auth_header = auth_header
            else:
                exc.status_code = status.HTTP_403_FORBIDDEN
        #获取异常处理函数,进入这个函数看一下
        exception_handler = self.get_exception_handler()
        #这个就是内容,view 啊 args啊 kwargs啊这些的,和上面得到的异常对象一起丢给下面的异常处理函数,由于上面的那个步骤,这里的异常处理函数已经是我们自己写的那个 exception_handler了。
        context = self.get_exception_handler_context()
        #异常函数:接收 exc, context,返回response
        response = exception_handler(exc, context)
        
        #如果是response不是none,就代表drf自己处理了。那什么时候会为none呢?看下面,当我们不自己配,用drf的异常处理的时候会怎么样。
        if response is None:
            #交给中间件处理,原生django处理
            self.raise_uncaught_exception(exc)
        
        #告诉前台是异常返回
        response.exception = True
        return response
    def get_exception_handler(self):
        """
        Returns the exception handler that this view uses.
        """
        #再熟悉不过了,你有,就找你的,没有就找系统的
        return self.settings.EXCEPTION_HANDLER

私たちは、時間の例外処理DRFと、自分のディストリビューションをしないとき、DRF独自の例外は、exception_handler機能を見て

def exception_handler(exc, context):
    """
    Returns the response that should be used for any given exception.

    By default we handle the REST framework `APIException`, and also
    Django's built-in `Http404` and `PermissionDenied` exceptions.

    Any unhandled exceptions may return `None`, which will cause a 500 error
    to be raised.
    """
    #这里判断异常是不是 属于 404 这种类型的
    if isinstance(exc, Http404):
        exc = exceptions.NotFound()
    #这里判断异常是不是关于权限的
    elif isinstance(exc, PermissionDenied):
        exc = exceptions.PermissionDenied()
    #这里判断异常是不是属于基类的,就是drf最大的异常收集范围了,如果超出了这个,就说明你的异常比这个基类还要大,就是原生的异常了,类似于什么没定义就调用某个变量这种的。
    if isinstance(exc, exceptions.APIException):
        headers = {}
        if getattr(exc, 'auth_header', None):
            headers['WWW-Authenticate'] = exc.auth_header
        if getattr(exc, 'wait', None):
            headers['Retry-After'] = '%d' % exc.wait

        if isinstance(exc.detail, (list, dict)):
            data = exc.detail
        else:
            data = {'detail': exc.detail}
        set_rollback()
        #如果处理的了的话,就返回一个response对象
        return Response(data, status=exc.status_code, headers=headers)
    #上面这个过程就相当于是drf在处理他能处理的范围,超出了能处理的范围的话,就返回none
    #处理不了的时候就返回none,让django来处理。
    return None

これらは、ときDRF処理されます

次に、我々は自分自身に対処するために持っているものを見て

カスタム例外処理のアップグレード版

升级版
from rest_framework.response import Response
from rest_framework.views import exception_handler as drf_exception_handler
def exception_handler(exc, context):
    #默认是drf可以解决的异常
    response = drf_exception_handler(exc, context)
    #当drf不能解决时,就会是none,这时候就直接返回一个响应
    if response is None: # drf没有处理的异常
        response = Response({'detail': '%s' % exc})

    # 项目阶段,要记录到日志文件
    return response

小概要

""" 源码分析
1、在APIView的dispatch方法中,有一个超大的try...except...,将代码运行异常都交给异常处理模块处理self.handle_exception(exc)
2、从配置中映射出配置处理异常的函数(自定义异常模块就是自定义配置指向自己的函数):self.get_exception_handler()
3、异常函数exception_handler(exc, context)处理异常,就会走自己的:
    先交给系统处理(客户端的异常),系统没处理(服务器异常),再自己处理
"""
"""
核心:异常信息都需要被logging记录,所以需要自定义;drf只处理客户端异常,服务器异常需要手动处理,统一处理结果
"""

応答モジュール

応答モジュールは、実際には応答なので、レスポンスのようなソースコードに直接移動します

class Response(SimpleTemplateResponse):
    """
    An HttpResponse that allows its data to be rendered into
    arbitrary media types.
    """
    #从升级版可以看出,这里的data就是你传进来的exc,status就是状态码
    def __init__(self, data=None, status=None,
                 template_name=None, headers=None,
                 exception=False, content_type=None):
        """
        Alters the init arguments slightly.
        For example, drop 'template_name', and instead use 'data'.

        Setting 'renderer' and 'media_type' will typically be deferred,
        For example being set automatically by the `APIView`.
        """
        super().__init__(None, status=status)

        if isinstance(data, Serializer):
            msg = (
                'You passed a Serializer instance as data, but '
                'probably meant to pass serialized `.data` or '
                '`.error`. representation.'
            )
            raise AssertionError(msg)

        self.data = data
        self.template_name = template_name
        self.exception = exception
        self.content_type = content_type

        if headers:
            for name, value in headers.items():
                self[name] = value

以前のバージョンの間違った、ステータスコードが200であるので、なぜ我々はそう彼に自分が応答ステータスコードを設定、それはない、最終版を考え出す必要があります。

アルティメットエディション:

""" 究极版
response = {
    'status': 7,
    'exc': '异常信息'
}
"""
from rest_framework.response import Response
from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework import status
def exception_handler(exc, context):
    response = drf_exception_handler(exc, context)

    if response is None: # drf没有处理的异常(服务器异常)
        #第二个参数就是500,
        return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR, data={
            'status': 7,
            'exc': '%s' % exc
        })

    # 项目阶段,要记录到日志文件
    return Response(status=response.status_code, data={
        'status': 7,
        # drf处理的客户端异常,原始处理方式是将异常信息放在response对象的data中,data的格式是{'datail': '具体的异常信息'}
        'exc': '%s' % response.data.get('detail')
    })

まあ、終わり。

おすすめ

転載: www.cnblogs.com/chanyuli/p/11898933.html