什么是Django中间件
中间件(Middleware)
是一个用来处理Django的请求(Request)
和响应(Response)
的框架级别的钩子,它是一个轻量、低级别的插件系统,用于在全局范围内改变Django的输入和输出。
当用户在网站中进行某个操作时,这个过程是用户向网站发送HTTP请求(Request);而网站会根据用户的操作发返回相关的网页内容,这个过程称为响应处理(Response)
。从请求到响应的过程中,当Django接收到用户请求时,首先经过中间件处理请求信息,执行相关的处理,然后将处理结果返回给用户。
从上图中可清晰的看到,中间件的作用是处理用户请求信息和返回响应内容。开发者可以根据自己的开发需求自定义中间件,只要将自定义的中间件添加到配置属性MIDDLEWARE
中即可激活,一般情况下,Django默认的中间件配置均可满足大部分开发需求
中间件的定义过程
中间件在settings.py
的配置属性MIDDLEWARE
中进行配置,在创建项目是,Django已默认配置了7个中间件,每个中间件的说明如下:
- SecurityMiddleware:内置的安全机制,保护用户与网站的通信安全
- SessionMiddleware:会话
Session
功能 - CommonMiddleware:处理请求信息,规范化请求内容
- CsrfViewMiddleware:开启CSRF防护功能
- AuthenticationMiddleware:开启内置的用户认证系统
- MessageMiddleware:开启内置的信息提示功能
- XFameOptionsMiddleware:防止恶意程序电脑及劫持
为了深入了解中间件的定义过程,我们在Pycharm
里打开并查看某个中间件的源码文件,分析中间件的定义过程,以中间件SessionMiddleware
为例,源码如下:
class SessionMiddleware(MiddlewareMixin):
def __init__(self, get_response=None):
self.get_response = get_response
engine = import_module(settings.SESSION_ENGINE)
self.SessionStore = engine.SessionStore
中间件SessionMiddleware
继承父类MiddlewareMixin
,父类MiddlewareMixin
只定义函数__init__
和__call__
,而中间件SessionMiddleware
除了重写父类的__init__
之外,还定义了钩子函数process_request
和process_response
。
一个完整的中间件设有5个钩子函数,Django将用户请求到网站响应的过程进行阶段划分,每个阶段对应执行某个钩子函数,每个钩子函数的运行说明如下。
- process_request():完成请求对象的创建,但用户访问的网址尚未与网站的路由地址匹配。
- process_view():完成用户访问的网址与路由地址的匹配,但尚未执行视图函数。
- process_exception():在执行视图函数的期间发生异常,比如代码异常,主动抛出404异常等。
- process_response():完成视图函数的执行,但尚未将响应内容返回浏览器
- process_template_response():默认不执行,在视图函数完成操作后调用,除非视图函数返回的response中有render方法(几乎不会用,可以忽略)
实战案例
先来做准备工作,新建一个项目middleware_demo
,创建一个子应用middleware_app
并在settings.py
中注册,首先配置好路由地址,代码如下
# middleware_demo.urls.py
urlpatterns = [
path('middleware/', include('middleware_app.urls')),
]
# middleware_app.urls.py
urlpatterns = [
path('', views.index, name="index")
]
路由配置完成后,创建视图函数index
,代码如下:
def index(request):
print("中间件首页")
return HttpResponse('中间件首页')
准备工作做完后,我们在middleware_app
应用中创建middlewares.py
文件,填写如下代码:
class FirstMiddleware(MiddlewareMixin):
def process_request(self, request):
"""
生成请求对象,路由匹配之前
:param request:
:return:
如果返回response: 调用当前中间件的process_response处理
如果返回None:调用下一个中间件的process_request处理
"""
print("firstMiddleware request")
def process_view(self, request, view_func, view_args, view_kwargs):
"""
路由匹配完成,视图函数调用之前
:param request:
:param view_func: url路由匹配到的视图函数
:param view_args: 视图函数的可变参数
:param view_kwargs: 视图函数的可变关键字参数
:return:
如果返回response:调用最后一个中间件的process_response开始处理
如果返回None:调用下一个中间件的process_view处理
"""
""""""
print("firstMiddleware process view")
def process_exception(self, request, exception):
"""
视图函数发生异常时
:param request:
:param exception: 处理过程中抛出的异常对象
:return:
如果返回response:之后的process_exception都不会触发,而是直接调用最后一个中间件的process_response处理
如果返回None:调用上一个中间件的process_exception处理
"""
print("firstMiddleware process exception")
def process_response(self, request, response):
"""
视图函数执行后,响应内容返回浏览器之前
:param request:
:param response:
:return:
response:调用上一个中间件的process_response处理
"""
print("firstMiddleware process response")
return response
然后将自定义的中间件插入到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',
'middleware_app.middlewares.FirstMiddleware',
]
因为中间件是有先后顺序的,所以我们一般自定义的都放在列表最后
接下来我们访问路由127.0.0.1/middleware/
,查看Pycharm
控制台的日志输出
firstMiddleware request
firstMiddleware process view
中间件首页
firstMiddleware process response
我们可以很清楚的看到请求的执行顺序,下面我们来总结一下
- 1.用户发送请求
- 2.执行process_request
- 3.urlconf路由匹配,找到对应的视图函数
- 4.执行视图预处理方法process_view
- 5.视图函数
- 6.process_template_response(如果视图函数返回的response,有render方法,没有则这一步不会执行)
- 7.执行process_response
- 8.返回response到用户
其中,在视图函数和process_template_response
处理过程中,如果出现 exception
,那么就会倒序执行中间件的process_exception
常见自定义中间件功能
总之,你如果有对全局request
或response
的操作需求,那么就可以使用中间件,例如:
- IP过滤:对一些特定IP地址返回特定响应
- URL过滤:如果用户访问的是
login
视图,则通过;如果访问其他视图,需要检测是不是有session
已经有了就通过,没有就返回login页面。这样就不用在多个视图函数上写装饰器login_required
- 内容压缩:response内容实现
gzip
的压缩,返回压缩后的内容给前端 - CDN:内容分发网络,实现缓存,如果缓存中有数据直接返回,没有找到缓存再去请求视图
- URL过滤:某个子应用升级暂停使用,某个特定的path路径下的请求,返回一个特定页面