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¶

原文地址:https://www.cnblogs.com/luozx207/p/13141845.html