一、什么是中间件
中间件是一个用来处理Django的请求和响应的框架级别的钩子。它是一个轻量、低级别的插件系统,用于在全局范围内改变Django的输入和输出。每个中间件组件都负责做一些特定的功能。
但是由于其影响的是全局,所以需要谨慎使用,使用不当会影响性能。
二、自定义中间件
中间件可以定义五个方法
process_request(self,request)
process_view(self, request, view_func, view_args, view_kwargs)
process_exception(self, request, exception)
process_template_response(self,request,response)
process_response(self, request, response)
自己定义一个中间件,需要写一个类,这个类必须继承 MiddlewareMixin
# views.py def index(request): print("views.py(视图函数)") return HttpResponse("ok")
# my_middleware.py from django.utils.deprecation import MiddlewareMixin class Test1Middleware(MiddlewareMixin): def process_request(self, request): print("Test1请求") def process_response(self, request, response): print("Test1返回") return response class Test2Middleware(MiddlewareMixin): def process_request(self, request): print("Test2请求") def process_response(self, request, response): print("Test2返回") return response
结果:
Test1请求
Test2请求
views.py(视图函数)
Test2返回
Test1返回
注意:
如果当请求到达 Test2请求 的时候直接不符合条件返回,即 return HttpResponse("Test2Middleware中断"),程序将把请求直接发给中间件 Test2Middleware 返回,然后依次返回到请求者:
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse class Test1Middleware(MiddlewareMixin): ... class Test2Middleware(MiddlewareMixin): def process_request(self, request): print("Test2请求") return HttpResponse("Test2Middleware中断") ...
结果:
Test1请求
Test2请求
Test2返回
Test1返回
process_request
该方法有一个参数,就是request。它的返回值可以是None也可以是HttpResponse对象。返回值是None的话,按正常流程继续走,交给下一个中间件处理,如果是HttpResponse对象,Django将不执行视图函数,而将相应对象直接返回给浏览器。
process_response
该方法有两个参数,一个是request,一个是response,request就是上述例子中一样的对象,response是视图函数返回的HttpResponse对象。该方法的返回值也必须是HttpResponse对象。
process_view
该方法有四个参数;
request HttpRequest对象
view_func Django即将使用的视图函数(它是实际的函数对象,而不是函数的名称作为字符串)
view_args 将传递给视图的位置参数的列表
view_kwargs 将传递给视图的关键字参数的字典;view_args和view_kwargs都不包含第一个视图参数(request)
class Test1Middleware(MiddlewareMixin): def process_request(self, request): print("Test1请求") def process_response(self, request, response): print("Test1返回") return response def process_view(self, request, view_func, view_args, view_kwargs): print("Test1_view") class Test2Middleware(MiddlewareMixin): def process_request(self, request): print("Test2请求") def process_response(self, request, response): print("Test2返回") return response def process_view(self, request, view_func, view_args, view_kwargs): print("Test2_view")
结果:
Test1请求
Test2请求
Test1_view
Test2_view
views.py(视图函数)
Test2返回
Test1返回
process_exception
该方法两个参数;
request HttpRequest对象
exception 视图函数异常产生的Exception对象
这个方法只有在视图函数中出现异常了才执行,它返回的值可以是一个None也可以是一个HttpResponse对象。如果是HttpResponse对象,Django将调用模板和中间件中的process_response方法,并返回给浏览器,否则将默认处理异常。如果返回一个None,则交给下一个中间件的process_exception方法来处理异常。
process_template_response(用的比较少)
该方法两个参数;
request HttpRequest对象
response TemplateResponse对象(由视图函数或者中间件产生)
process_template_response是在视图函数执行完成后立即执行,但是它有一个前提条件,那就是视图函数返回的对象有一个render()方法(或者表明该对象是一个TemplateResponse对象或等价方法)
class MD1(MiddlewareMixin): def process_request(self, request): print("MD1里面的 process_request") def process_response(self, request, response): print("MD1里面的 process_response") return response def process_view(self, request, view_func, view_args, view_kwargs): print("-" * 80) print("MD1 中的process_view") print(view_func, view_func.__name__) def process_exception(self, request, exception): print(exception) print("MD1 中的process_exception") return HttpResponse(str(exception)) def process_template_response(self, request, response): print("MD1 中的process_template_response") return response class MD2(MiddlewareMixin): def process_request(self, request): print("MD2里面的 process_request") pass def process_response(self, request, response): print("MD2里面的 process_response") return response def process_view(self, request, view_func, view_args, view_kwargs): print("-" * 80) print("MD2 中的process_view") print(view_func, view_func.__name__) def process_exception(self, request, exception): print(exception) print("MD2 中的process_exception") def process_template_response(self, request, response): print("MD2 中的process_template_response") return response
def index(request): print("app01 中的 index视图") def render(): print("in index/render") return HttpResponse("O98K") rep = HttpResponse("OK") rep.render = render return rep
MD2里面的 process_request MD1里面的 process_request -------------------------------------------------------------------------------- MD2 中的process_view <function index at 0x000001C111B97488> index -------------------------------------------------------------------------------- MD1 中的process_view <function index at 0x000001C111B97488> index app01 中的 index视图 MD1 中的process_template_response MD2 中的process_template_response in index/render MD1里面的 process_response MD2里面的 process_response
三、应用案例
①做IP访问频率限制
某些IP访问服务器的频率过高,进行拦截,比如限制每分钟不能超过20次。
②URL访问过滤
如果用户访问的是login视图(放行);
如果访问其他视图,需要检测是不是有session认证,已经有了放行,没有返回login,这样就省得在多个视图函数上写装饰器了!
中间件版登录校验
urls.py
from django.conf.urls import url from appxx import views urlpatterns = [ url(r'^index/', views.index), url(r'^home/', views.home), url(r'^login/', views.login), ]
views.py
from django.shortcuts import HttpResponse from django.shortcuts import redirect from appxx import models def login(request): if request.method == "POST": user = request.POST.get("username") pwd= request.POST.get("password") obj = models.UserInfo.objects.filter(username=user, password=pwd) if obj: request.session["user"] = user request.session.set_expiry(30) # session会在30秒数后失效 next_url = request.GET.get("next") if next_url: return redirect(next_url) else: return redirect("/home/") return render(request, "login.html") def index(request): return HttpResponse("This's index.html.") def home(request): return HttpResponse("This's home.html.")
my_middleware.py
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse from django.shortcuts import redirect class CheckLoginMiddleware(MiddlewareMixin): white_list = ["/login/"] black_list = ["/black/"] def process_request(self, request): next_nrl = request.path_info if next_nrl in self.white_list or request.session.get("user"): return # 只写return,相当于结束一个函数的继续 elif next_nrl in self.black_list: return HttpResponse("This's an illegal URL.") else: return redirect("/login/?next={}".format(next_nrl))
login.html
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="{{ request.get_full_path }}" method="post"> {% csrf_token %} <p> <label for="username">账号:</label> <input type="text" id="username" name="username"> </p> <p> <label for="password">密码:</label> <input type="password" id="password" name="password"> </p> <p> <label for="submit"></label> <input type="submit" value="登录"> </p> </form> </body> </html>
CheckLoginMiddleware 中间件祖册后,所有的请求都要走 CheckLoginMiddleware 的 process_request 方法;访问的URL在白名单内或者 session 中有 xxx 用户名,则不做阻拦,走正常流程;如果URL在黑名单中,则返回 "This's an illegal URL." 的字符串;其他正常的URL如果需要登录后才能访问,则让浏览器跳转到登录页面。