Django之中间件
一 何为中间件
Django的中间件(middleware)实质就是一个类,在请求到来和结束后,django会根据自己的规则在合适的时机执行中间件中相应的方法。
在Django项目是settings模板中,有一个 MIDDLEWARE(在1.10之前的版本中,中间件的关键字为:MIDDLEWARE_CLASSES) 变量,其中每一个元素就是一个中间件(Django内部自带),如下:
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',
]
在中间件这个类中,可以定义以下5种方法:
process_request(self, request) process_view(self, request, callback, callback_args, callback_kwargs) process_template_response(self, request, response) process_response(self, request, response) process_exception(self, request, response, exception)
我们从浏览器发出一个请求 Request,得到一个响应后的内容 HttpResponse ,这个请求传递到 Django的过程如下:
也就是说,每一个请求都是先通过中间件中的 process_request 函数,这个函数返回 None 或者 HttpResponse 对象,如果返回前者,继续处理其它中间件;如果返回一个 HttpResponse,就处理中止,返回到网页上(详情请见后续分析)。
ps:中间件在其他web框架中,有的叫管道,httphandle
二 中间件源码查看
2.1 如何进入中间件
根据MIDDLEWARE元素对象,我们可以任意选择一个元素做模块导入操作(这里选择比较熟悉的csrf):
from django.middleware.csrf import CsrfViewMiddleware
2.2 CsrfViewMiddleware类
class CsrfViewMiddleware(MiddlewareMixin): #该类继承了MiddlewareMixin
"""
Middleware that requires a present and correct csrfmiddlewaretoken
for POST requests that have a CSRF cookie, and sets an outgoing
CSRF cookie.
This middleware should be used in conjunction with the csrf_token template
tag.
"""
# The _accept and _reject methods currently only exist for the sake of the
# requires_csrf_token decorator.
def _accept(self, request):
pass
def _reject(self, request, reason):
pass
def process_view(self, request, callback, callback_args, callback_kwargs):
pass
def process_response(self, request, response):
pass
2.3 MiddlewareMixin 类
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)
if not response:
response = self.get_response(request)
if hasattr(self, 'process_response'):
response = self.process_response(request, response)
return response
三 自定义中间件
3.1 创建中间件类文件(工程主目录下my_middleware.py)
from django.utils.deprecation import MiddlewareMixin #必须导入MiddlewareMixin且继承,或者将其源码粘到自己的py文件中自建 class MyMiddleware1(MiddlewareMixin): def process_request(self,request): pass def process_response(self,request,response): return response #其他方法略
注意:process_respons返回值是必须需要的,因为执行过程中视图返回的HttpResponse的返回值就是通过process_response一层层的向上return的,所以process_response的返回值没有代码则会报错;相反,如果process_request中返回return,则函数立即返回。
3.2 注册中间件
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',
'my_middleware.MyMiddleware1', #自定义中间件注册
'my_middleware.MyMiddleware2', #自定义中间件注册
'my_middleware.MyMiddleware3', #自定义中间件注册
]
注意:执行时一直报下面的错误,原来是在注册时,各中间件元素没有加逗号导致
ImportError: No module named 'my_middleware.MyMiddleware1my_middleware'; 'my_middleware' is not a package
四 process_request&process_response(***)
创建中间件py文件
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
class MyMiddleware1(MiddlewareMixin):
def process_request(self, request):
print("中间件1请求")
def process_response(self, request, response):
print("中间件1返回")
return response
class MyMiddleware2(MiddlewareMixin):
def process_request(self, request):
print("中间件2请求")
# return HttpResponse('立即返回')
def process_response(self, request, response):
print("中间件2返回")
return response
class MyMiddleware3(MiddlewareMixin):
def process_request(self, request):
print("中间件3请求")
def process_response(self, request, response):
print("中间件3返回")
return response
创建视图函数
from django.shortcuts import render,HttpResponse
def test(request):
print('执行视图函数')
return HttpResponse('OK')
客户端发起请求时,后台结果如下:
但如果当请求到达中间件2的时候return,程序将把请求直接发给中间件2返回,然后依次返回到请求者,结果如下:
图解如下:
注意:在Django1.10之前的版本是直接返回到最后一个中间件的response,然后向上依次返回,最后到发起请求
五 process_view
在自定义的中间件文件中的每个类中加入process_view方法:
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
class MyMiddleware1(MiddlewareMixin):
def process_request(self, request):
print("中间件1请求")
def process_response(self, request, response):
print("中间件1返回")
return response
def process_view(self, request, callback, callback_args, callback_kwargs):
print("中间件1view")
class MyMiddleware2(MiddlewareMixin):
def process_request(self, request):
print("中间件2请求")
#return HttpResponse('立即返回') # 如若此处加入return,程序立即返回
def process_response(self, request, response):
print("中间件2返回")
return response
def process_view(self, request, callback, callback_args, callback_kwargs):
print("中间件2view")
#return HttpResponse('view2返回')
class MyMiddleware3(MiddlewareMixin):
def process_request(self, request):
print("中间件3请求")
def process_response(self, request, response):
print("中间件3返回")
return response
def process_view(self, request, callback, callback_args, callback_kwargs):
print("中间件3view")
后台执行结果如下:
但如果当请求到达中间件2的view方法时return,程序将把请求直接发给最后一个中间件的response方法,然后依次返回到请求者,结果如下:
图解如下:
补充:process——view可以用来调用视图函数
类MyMiddleware1做如下修改:
class MyMiddleware1(MiddlewareMixin): def process_request(self, request): print("中间件1请求") def process_response(self, request, response): print("中间件1返回") return response def process_view(self, request, callback, callback_args, callback_kwargs): print("中间件1view") # return HttpResponse('立即返回') response = callback(request,*callback_args,**callback_kwargs) return response )
执行结果如下:

六 process_exception
当views的函数中出现错误时,就会执行process_exception方法。
在自定义的中间件文件中的每个类中加入process_exception方法:
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
class MyMiddleware1(MiddlewareMixin):
def process_request(self, request):
print("中间件1请求")
def process_response(self, request, response):
print("中间件1返回")
return response
def process_view(self, request, callback, callback_args, callback_kwargs):
print("中间件1view")
def process_exception(self, request, exception):
return HttpResponse("中间件1错误")
class MyMiddleware2(MiddlewareMixin):
def process_request(self, request):
print("中间件2请求")
#return HttpResponse('立即返回') # 如若此处加入return,程序立即返回
def process_response(self, request, response):
print("中间件2返回")
return response
def process_view(self, request, callback, callback_args, callback_kwargs):
print("中间件2view")
#return HttpResponse('view2返回')
# def process_exception(self, request, exception):
# return HttpResponse("中间件2错误")
class MyMiddleware3(MiddlewareMixin):
def process_request(self, request):
print("中间件3请求")
def process_response(self, request, response):
print("中间件3返回")
return response
def process_view(self, request, callback, callback_args, callback_kwargs):
print("中间件3view")
# def process_exception(self, request, exception):
# return HttpResponse("中间件3错误")
根据结果图解如下:
七 process_template_responseprocess
只有当views函数中返回的对象中具有render方法时,才会执行process_template_responseprocess方法
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
class MyMiddleware1(MiddlewareMixin):
def process_request(self, request):
print("中间件1请求")
def process_response(self, request, response):
print("中间件1返回")
return response
def process_template_response(self, request, response):
print('中间件1 process_template_response')
return response
class MyMiddleware2(MiddlewareMixin):
def process_request(self, request):
print("中间件2请求")
def process_response(self, request, response):
print("中间件2返回")
return response
def process_template_response(self, request, response):
print('中间件2 process_template_response')
return response
class MyMiddleware3(MiddlewareMixin):
def process_request(self, request):
print("中间件3请求")
def process_response(self, request, response):
print("中间件3返回")
return response
def process_template_response(self, request, response):
print('中间件3 process_template_response')
return response
from django.shortcuts import render,HttpResponse
class Foo():
def __init__(self,req):
self.req = req
def render(self):
return HttpResponse('...')
def test(request):
print('执行视图函数')
obj=Foo(request)
return obj
客户端访问结果:
相关应用:
既然process_template_respnse,不返回视图函数的return的结果,而是返回视图函数 return值(对象)的render方法的返回值,我们就可以在 这个视图函数返回对象的 render方法里,做返回值的二次加工!
例如:我们可以封装返回数据,需要时直接调用即可。
from django.shortcuts import render,HttpResponse
class JsonResponse:
def __init__(self, req, status, msg):
self.req = req
self.status = status
self.msg = msg
def render(self):
import json
ret = {
'status':self.status,
'msg':self.msg
}
return HttpResponse(json.dumps(ret))
def test(request):
obj = JsonResponse(request, True, '返回信息')
return obj
八 实现新旧Django版本的兼容代码
try:
from django.utils.deprecation import MiddlewareMixin # Django 1.10.x
except ImportError:
MiddlewareMixin = object # Django 1.4.x - Django 1.9.x
class MyMiddleware(MiddlewareMixin):
def process_request(self, request):
pass
def process_response(request, request, response):
pass
九 中间件应用场景
中间件工作在视图函数执行前、执行后(类似所有视图函数的装饰器!),适合所有的请求/一部分请求做批量处理
9.1 ip限制
放在中间件类的列表中,阻止某些IP访问目标网址
9.2 URL访问过滤
如果用户访问的是登录视图,正常访问;
如果访问其他视图,则检测是不是存在session,如存在则正常访问,如不存在,则返回登录视图。这样我们就可以不用再多个视图函数上写装饰器了
9.3 缓存
客户端请求来了,中间件去缓存查看是否存在需要的数据,如存在直接返回给用户,不存在再去逻辑层 执行视图函数










浙公网安备 33010602011771号