zoukankan      html  css  js  c++  java
  • Django中间件

    执行顺序

    按照settings.MIDDLEWARE的顺序,由上至下执行,如:

    MIDDLEWARE = [
        "django.middleware.security.SecurityMiddleware",
        "django.middleware.common.CommonMiddleware",
        "django.middleware.clickjacking.XFrameOptionsMiddleware",
        "apps.api.middleware_mgr.Middleware",
    ]
    
    # django/core/handlers/base.py
    
    class BaseHandler(object):
        def load_middleware(self):
            # _get_response会逐层调用中间件的process_view方法
            # 然后处理view,即中间件的最中心
            handler = convert_exception_to_response(self._get_response)
            for middleware_path in reversed(settings.MIDDLEWARE):
                # 按照 MIDDLEWARE 的逆序生成中间件实例
                middleware = import_string(middleware_path)
                try:
                    # 这里将中间件实例当做函数调用,即MiddlewareMixin的__call__方法
                    # 由于是按逆序生成中间件实例,因此是一层层外套用
                    mw_instance = middleware(handler)
                except MiddlewareNotUsed as exc:
                    ...
    
                if hasattr(mw_instance, 'process_view'):
                    # process_view是正序调用
                    self._view_middleware.insert(0, mw_instance.process_view)
                if hasattr(mw_instance, 'process_template_response'):
                    # process_template_response 和 process_exception 都是逆序调用
                    self._template_response_middleware.append(mw_instance.process_template_response)
                if hasattr(mw_instance, 'process_exception'):
                    self._exception_middleware.append(mw_instance.process_exception)
    
                # 这边将当前层的中间件实例作为外一层的get_response函数
                # 传入MiddlwareMixin的__init__中
                # 这样保证了process_request和process_response的正确调用顺序
                handler = convert_exception_to_response(mw_instance)
    

    中间件发生错误后的返回

    中间件发生错误后将直接返回一个HttpResponse对象,下层中间件将不会被执行

    class MiddlewareMixin(object):
        def __init__(self, get_response=None):
            self.get_response = get_response
            super(MiddlewareMixin, self).__init__()
    
        def __call__(self, request):
            response = None
            if hasattr(self, 'process_request'):
                response = self.process_request(request)
            # 当response不为空时,将不会执行下层中间件
            if not response:
                # 没有发生错误,继续执行下层中间件
                response = self.get_response(request)
            # 直接执行当前层的proces_response,并返回响应
            if hasattr(self, 'process_response'):
                response = self.process_response(request, response)
            return response
    

    自定义中间件

    五大钩子函数

    表头 表头 表头 表头
    process_request 请求刚到来,执行视图之前 配置列表的正序 None或者HttpResponse对象
    process_response 视图执行完毕,返回响应时 逆序 HttpResponse对象
    process_view process_request之后,路由转发到视图,执行视图之前 正序 None或者HttpResponse对象
    process_exception 视图执行中发生异常时 逆序 None或者HttpResponse对象
    process_template_response 视图刚执行完毕,process_response之前 逆序 实现了render方法的响应对象

    例子

    注意middleware方法的输入参数都是固定的

    # utils/my_middleware.py
    from django.utils.deprecation import MiddlewareMixin
    
    
    class MyMiddleware1(MiddlewareMixin):
        def __str__(self):
            return "MyMiddleware1"
    
        def process_request(self, request):
            print(self, " processing request...")
    
        def process_view(self, request, view_func, view_args, view_kwargs):
            print(self, " processing view...")
    
        def process_response(self, request, response):
            print(self, " processing response...")
            return response
    
        def process_exception(self, request, exception):
            print(self, " processing exception...")
    
    
    class MyMiddleware2(MiddlewareMixin):
        def __str__(self):
            return "MyMiddleware2"
    
        def process_request(self, request):
            print(self, " processing request...")
    
        def process_view(self, request, view_func, view_args, view_kwargs):
            print(self, " processing view...")
    
        def process_response(self, request, response):
            print(self, " processing response...")
            return response
    
        def process_exception(self, request, exception):
            print(self, " processing exception...")
    
    # settings.py
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
        'utils.my_middleware.MyMiddleware1',
        'utils.my_middleware.MyMiddleware2'
    ]
    
    # urls.py
    from django.conf.urls import url
    
    from demo.views import mid_test
    
    urlpatterns = [
        url(r"^midtest/", mid_test),
    ]
    
    # views.py
    from django.http import HttpResponse
    
    def mid_test(request):
        print('in mid_test view')
        return HttpResponse('ok')
    

    访问 http://127.0.0.1:8000/midtest/ ,可在控制台得到这样的输出,验证了之前关于中间件执行顺序的理论

    MyMiddleware1  processing request...
    MyMiddleware2  processing request...
    MyMiddleware1  processing view...
    MyMiddleware2  processing view...
    in mid_test view
    MyMiddleware2  processing response...
    MyMiddleware1  processing response...
    [08/Jun/2020 17:32:54] "GET /midtest/ HTTP/1.1" 200 2
    

    中间件的中断

    如果process_request返回了一个HttpResponse对象

    class MyMiddleware1(MiddlewareMixin):
        def __str__(self):
            return "MyMiddleware1"
    
        def process_request(self, request):
            print(self, " processing request...")
            return HttpResponse("break")
    

    下层中间件将不会被执行,response会按原路返回

    MyMiddleware1  processing request...
    MyMiddleware1  processing response...
    [08/Jun/2020 18:08:21] "GET /midtest/ HTTP/1.1" 200 5
    

    如果process_view返回了一个HttpResponse对象,同样

    class MyMiddleware1(MiddlewareMixin):
        def __str__(self):
            return "MyMiddleware1"
    
        def process_request(self, request):
            print(self, " processing request...")
            return HttpResponse("break")
    
    MyMiddleware1  processing request...
    MyMiddleware2  processing request...
    MyMiddleware1  processing view...
    MyMiddleware2  processing response...
    MyMiddleware1  processing response...
    [08/Jun/2020 18:11:00] "GET /midtest/ HTTP/1.1" 200 5
    

    视图异常

    如果视图引发了异常,如

    from django.http import HttpResponse
    
    def mid_test(request):
        print('in mid_test view')
        1/0
        return HttpResponse('ok')
    

    则Django会逆序调用中间件的process_exception方法,紧接着逆序调用process_response方法

    MyMiddleware1  processing request...
    MyMiddleware2  processing request...
    MyMiddleware1  processing view...
    MyMiddleware2  processing view...
    in mid_test view
    MyMiddleware2  processing exception...
    MyMiddleware1  processing exception...
    Internal Server Error: /midtest/
    Traceback (most recent call last):
      File "/home/demo/venv/lib/python3.6/site-packages/django/core/handlers/exception.py", line 41, in inner
        response = get_response(request)
      File "/home/demo/venv/lib/python3.6/site-packages/django/core/handlers/base.py", line 187, in _get_response
        response = self.process_exception_by_middleware(e, request)
      File "/home/demo/venv/lib/python3.6/site-packages/django/core/handlers/base.py", line 185, in _get_response
        response = wrapped_callback(request, *callback_args, **callback_kwargs)
      File "/home/demo/views.py", line 6, in mid_test
        1/0
    ZeroDivisionError: division by zero
    MyMiddleware2  processing response...
    MyMiddleware1  processing response...
    

    如果process_exception返回了一个HttpResponse对象

    class MyMiddleware2(MiddlewareMixin):
        def process_exception(self, request, exception):
            print(self, " processing exception...")
            return HttpResponse('not ok')
    

    页面会显示process_exception返回的内容,而不再使用默认异常处理。并且,此中间件之上的中间件类的process_exception方法不会被调用。但是process_response方法仍然会被逆序调用

    MyMiddleware1  processing request...
    MyMiddleware2  processing request...
    MyMiddleware1  processing view...
    MyMiddleware2  processing view...
    in mid_test view
    MyMiddleware2  processing exception...
    MyMiddleware2  processing response...
    MyMiddleware1  processing response...
    

    参考资料

    Django中间件 --刘江

    Middleware¶

  • 相关阅读:
    (转)Silverlight从程序集读取xaml文件
    阻止jQuery事件冒泡
    如何避免JQuery Dialog的内存泄露(转)
    VS2010 好用的javascript扩展工具
    C#计时器
    Silverlight初体验之设置启动页面
    javascript调用asp.net后置代码方法
    应用程序工程文件组织重要性
    javascript中字符串转化为json对象
    jQuery EasyUI
  • 原文地址:https://www.cnblogs.com/luozx207/p/13141845.html
Copyright © 2011-2022 走看看