Django中间件

Django中间件是一种轻量级的插件系统,全局调整输入和输出。中间件执行流程包括:request方法到达视图,其他方法,response方法返回客户端。自定义中间件时,request方法不应返回,否则中断请求。中间件执行顺序:process_request(可能不返回或返回None)、process_view(执行视图函数前)、process_template_response(视图返回render时执行)、process_exception(视图出错时)。中间件适用于IP限制、URL过滤、缓存等场景。中间件工厂函数创建可调用对象,请求前后执行顺序相反。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

中间件是插在 Django 的请求和响应过程之中的框架。这是一种轻量级的低层插件系统,用于全局调整 Django的输入或输出。
在django中,中间件其实就是一个类。在http请求到达视图函数之前和视图函数return之后,django会根据自己的规则在合适的时机执行中间件中相应的方法。

中间件的执行流程
1、执行完所有的request方法 到达视图函数。
2、执行中间件的其他方法
3、经过所有response方法 返回客户端。

一、自定义中间件

1.在project下随便创建一个M.py文件

from django.utils.deprecation import MiddlewareMixin
class Middle1(MiddlewareMixin):
	def process_request(self,request):
        print(" process_reques")
    def process_response(self, request,response):
        print('process_response')

2、在setings文件中 注册这个 py文件


MIDDLEWARE = [
	............
  'django.middleware.cache.FetchFromCacheMiddleware',   
  'M1.Middle1'
]
process_request
process_response
Internal Server Error: /books/search/
Traceback (most recent call last):
  File "D:\env_django_core\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
    response = get_response(request)
  File "D:\env_django_core\lib\site-packages\django\utils\deprecation.py", line 93, in __call__
    response = self.process_response(request, response)
  File "D:\env_django_core\lib\site-packages\django\middleware\clickjacking.py", line 26, in process_response
    if response.get('X-Frame-Options') is not None:
AttributeError: 'NoneType' object has no attribute 'get'

因为 自定义的中间件response方法没有return,交给下一个中间件,导致http请求中断了!!!
注意 自定义的中间件request 方法不要return 因为返回值中间件不再往下执行,导致 http请求到达不了视图层,因为request在视图之前执行!

正确写法:

from django.utils.deprecation import MiddlewareMixin
class Middle1(MiddlewareMixin):
	# 不用return Django内部自动帮我们传递
	# 或者return None 会继续执行剩余中间件
    def process_request(self,request):
        print("process_request") 
    def process_response(self, request,response):
        print('process_response')
        return response #执行完了这个中间件一定要 传递给下一个中间件

二、中间件执行顺序

在这里插入图片描述上图中错误:
1、就是在process_request中有返回值的时候,是从当前的中间件的response开始返回的。
2、路由系统是在process_view之前执行的的,因为process_view要拿到需要执行视图函数的信息。

process_request(self,request)
process_view(self, request, callback, callback_args, callback_kwargs)
process_template_response(self,request,response)
process_exception(self, request, exception)
process_response(self, request, response)

MIDDLEWARE = [
	............
  'django.middleware.cache.FetchFromCacheMiddleware',   
  'M1.Middle',
  'M2.Middle',
  'M3.Middle',
]

1、process_request(self,request)
一般不返回值或者返回None。如果有返回值,直接跳转到当前中间件的process_response,依次执行后面的process_response。

#  M2 process_request 有返回值
def process_request(self,request):
    print("M2: process_request")
    return HttpResponse('123', status=200)
#  略过所有的其他方法,直接跳转到当前中间件的process_response。
#  M3的process_response被跳过
M1: process_request
M2: process_request
#  没有执行视图函数
M2: process_response
M1: process_response

2、 process_view(self, request, callback, callback_args, callback_kwargs)方法介绍

(1)执行完所有中间件的request方法
(2)url匹配成功
(3)拿到 视图函数的名称、参数,(注意不执行) 再执行process_view()方法
(4)最后去执行视图函数

def process_view(self, request,callback,callback_args,callback_kwargs ):
    print("M1: process_view")
M1: process_request
M2: process_request
M3: process_request
M1: process_view
M2: process_view
M3: process_view
执行view视图函数
M3: process_response
M2: process_response
M1: process_response

既然 process_view 拿到视图函数的名称、参数,(不执行) 再执行process_view()方法,最后才去执行视图函数!

那可以在 执行process_view环节直接 把函数执行返回吗?

def process_view(self, request,callback,callback_args,callback_kwargs ):
    print("M2.process_view")
    response=callback(request,*callback_args,**callback_kwargs)
    return response

M1.process_view->M2.process_view->M1.process_response
跳过了M3的view直接返回了。

# 
M1: process_request
M2: process_request
M3: process_request
M1: process_view
M2: process_view
执行view视图函数
M3: process_response
M2: process_response
M1: process_response

3、process_exception(self, request, exception)方法

def process_exception(self, request, exception):
    print('M1: process_exception')
M1: process_request
M2: process_request
M3: process_request
M1: process_view
M2: process_view
M3: process_view
执行view视图函数
M3: process_response
M2: process_response
M1: process_response

并没有执行process_exception, 因为rocess_exception默认不执行,process_exception方法在视图函数执行出错的时候才会执行。

M1: process_request
M2: process_request
M3: process_request
M1: process_view
M2: process_view
M3: process_view
执行view视图函数
M3: process_exception
M2: process_exception
M1: process_exception
Internal Server Error: /books/search/
Traceback (most recent call last):
  File "D:\env_django_core\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
    response = get_response(request)
  File "D:\env_django_core\lib\site-packages\django\core\handlers\base.py", line 126, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "D:\env_django_core\lib\site-packages\django\core\handlers\base.py", line 124, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "D:\django_core_mysite\books\views.py", line 12, in search
    a = b
NameError: name 'b' is not defined
M3: process_response
M2: process_response
M1: process_response
[08/May/2019 18:14:14] "GET /books/search/ HTTP/1.1" 500 68104

(1) 执行完所有 request 方法
(2) 执行 所有 process_view方法
(3) 如果视图函数出错,执行process_exception(最终response,process_exception的return值)。如果process_exception 方法有了 返回值 就不再执行 其他中间件的 process_exception,直接执行response方法响应
(4) 执行所有response方法
(5) 最后返回process_exception的返回值

def process_exception(self, request, exception):
   print('M2: process_exception')
   return HttpResponse('M2: process_exception')
M1: process_request
M2: process_request
M3: process_request
M1: process_view
M2: process_view
M3: process_view
执行view视图函数
M3: process_exception
M2: process_exception  # M2的 process_exception 有返回值
# M1 的 process_exception没执行, 直接到process_response
M3: process_response
M2: process_response
M1: process_response

浏览器显示 M2: process_exception

3、process_template_response()

如果视图函数中的返回值 中有render方法,才会执行 process_template_response

def process_template_response(self,request,response):
    print('M2process_template_response')
	return response
# 视图函数
def search(request):
    print('执行view视图函数')
    return render(request, 'search.html', {'error': error})
M1: process_request
M2: process_request
M3: process_request
M1: process_view
M2: process_view
M3: process_view
执行view视图函数
# 并没有执行process_template_response,
# 因为 视图函数返回的对象并没有 render()方法
M3: process_response
M2: process_response
M1: process_response
[09/May/2019 11:37:56] "GET /books/search/ HTTP/1.1" 200 4274

视图函数返回的对象有render() 方法:

from django.shortcuts import render,HttpResponse

# Create your views here.
class Foo():
    def __init__(self,requ):
        self.req = requ
    def render(self):
        return HttpResponse(“I'm render!”)

def index(request):
    print("执行view视图函数")
    obj = Foo(request)
    return obj
M1: process_request
M2: process_request
M3: process_request
M1: process_view
M2: process_view
M3: process_view
执行view视图函数
M3: process_template_response
M2: process_template_response
M1: process_template_response
M3: process_response
M2: process_response
M1: process_response
[09/May/2019 11:41:43] "GET /books/search/ HTTP/1.1" 200 12

浏览器显示: 'I’m render!
把对象的render方法的返回值返回给用户(注意不返回视图函数的return的结果了,而是返这个结果对象里render方法的返回值,并且process_template_response方法也要返回response不然会报错。

可以这么玩:
如果多个view需要处理类似的信息,可以在process_template_response对msg进行统一处理。

class Foo():
    def __init__(self, requ, res, msg):
        self.req = requ
        self.res = res
        self.msg = msg
    def deal_with_msg(self):
        pass
    def render(self):
        return self.res

def search(request):
    print("执行view视图函数")
    error = False
    res = render(request, 'search.html', {'error': error})
    msg = ''
    obj = Foo(request, res, msg)
    return obj

三、中间件应用场景

由于中间件工作在 视图函数执行前、执行后(像不像所有视图函数的装饰器!)适合所有的请求/一部分请求做批量处理

1、做IP限制

放在 中间件类的列表中,阻止某些IP访问了;

2、URL访问过滤

如果用户访问的是login视图(放过)

如果访问其他视图(需要检测是不是有session已经有了放行,没有返回login),这样就省得在 多个视图函数上写装饰器了!

3、缓存

客户端请求来了,中间件去缓存看看有没有数据,有直接返回给用户,没有再去逻辑层 执行视图函数

四、中间件钩子函数

定义一个中间件工厂函数,然后返回一个可以别调用的中间件。中间件工厂函数需要接收一个可以调用的get_response对象。返回的中间件也是一个可以被调用的对象,并且像视图一样需要接收一个request对象参数,返回一个response对象。

def simple_middleware(get_response):
    # 此处编写的代码仅在Django第一次配置和初始化的时候执行一次, 类似before_first_request

    def middleware(request):
        # 此处编写的代码会在每个请求处理视图前被调用, 类似before_request

        response = get_response(request)

        # 此处编写的代码会在每个请求处理视图之后被调用,类似after_request

        return response

    return middleware

例如,在子应用users应用中新建一个middleware.py文件,
def my_middleware(get_response):
    print('init 被调用')
    def middleware(request):
        print('before request 被调用')
        response = get_response(request)
        print('after response 被调用')
        return response
    return middleware
定义好中间件后,需要在工程目录下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',
    'users.middleware.my_middleware',  # 添加中间件
]

在请求视图被处理前,中间件由上至下依次执行

在请求视图被处理后,中间件由下至上依次执行

def my_middleware(get_response):
    print('init 被调用')
    def middleware(request):
        print('before request 被调用')
        response = get_response(request)
        print('after response 被调用')
        return response
    return middleware

def my_middleware2(get_response):
    print('init2 被调用')
    def middleware(request):
        print('before request 2 被调用')
        response = get_response(request)
        print('after response 2 被调用')
        return response
    return middleware


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',
    'users.middleware.my_middleware',  # 添加
    'users.middleware.my_middleware2',  # 添加
]

执行结果

init2 被调用
init 被调用
before request 被调用
before request 2 被调用
view 视图被调用
after response 2 被调用
after response 被调用

https://www.cnblogs.com/buyisan/p/8557252.html

https://www.cnblogs.com/skaarl/p/9457017.html?utm_source=debugrun&utm_medium=referral

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值