day70 Django 中间件
前戏
我们在前面的课程中已经学会了给视图函数加装饰器来判断是用户是否登录,把没有登录的用户请求跳转到登录页面。我们通过给几个特定视图函数加装饰器实现了这个需求。但是以后添加的视图函数可能也需要加上装饰器,这样是不是稍微有点繁琐。
中间件
什么是中间件?
官方的说法:中间件是一个用来处理Django的请求和响应的框架级别的钩子。它是一个轻量、低级别的插件系统,用于在全局范围内改变Django的输入和输出。每个中间件组件都负责做一些特定的功能。 但是由于其影响的是全局,所以需要谨慎使用,使用不当会影响性能。
我们一直都在使用中间件,只是没有注意到而已,打开Django项目的Settings.py文件,看到下图的MIDDLEWARE配置项。

Django请求生命周期
中间件能干什么?
登录验证 访问限制 ... 每个视图函数都要做的事儿就可以提到中间件来做
自定义中间件
中间件可以定义五个方法,分别是:(主要的是process_request和process_response)
process_request(self,request) process_view(self, request, view_func, view_args, view_kwargs) process_template_response(self,request,response) process_exception(self, request, exception) process_response(self, request, response) 以上方法的返回值可以是None或一个HttpResponse对象,如果是None,则继续按照django定义的规则向后继续执行,如果是HttpResponse对象,则直接将该对象返回给用户。
自定义一个中间件示例
from django.utils.deprecation import MiddlewareMixin class MD1(MiddlewareMixin): def process_request(self, request): print("MD1里面的 process_request") def process_response(self, request, response): print("MD1里面的 process_response") return response
在settings.py的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', 'middlewares.MD1', # 自定义中间件MD1 ]
process_request(self,request)
1. 执行时间和顺序 process_request方法在视图函数之前执行 当配置多个中间件时,会按照MIDDLEWARE中的注册顺序,也就是列表的索引值,从前到后依次执行的。 2. 参数 process_request有一个参数,就是request,这个request和视图函数中的request是一样的。 不同中间件之间传递的request都是同一个对象 3. 返回值 1. 返回None,请求继续往后执行,交给下一个中间件处理 2. 返回HttpResponse对象,请求直接就结束了,Django将不执行视图函数,而将相应对象返回给浏览器。
process_response(self, request, response)
1. 执行时间和执行顺序 process_response方法在视图函数之后执行 多个中间件中的process_response方法是按照MIDDLEWARE中的注册顺序倒序执行的,也就是说第一个中间件的process_request方法首先执行,而它的process_response方法最后执行,最后一个中间件的process_request方法最后一个执行,它的process_response方法是最先执行。 2. 参数 request --> 请求 response --> 响应对象 3. 返回值 response是视图函数返回的HttpResponse对象,该方法的返回值也必须返回response对象

process_view(self, request, view_func, view_args, view_kwargs)
1. 执行时间和执行顺序 process_view方法是在中间件的process_request方法之后 在视图函数之前执行,具体是在urls.py中得到将要执行的视图函数之后执行。 执行顺序按照MIDDLEWARE中的注册顺序从前到后顺序执行的 2. 参数 request --> 是HttpRequest对象。 view_func --> 将要执行的视图函数对象 (它是实际的函数对象,而不是函数的名称作为字符串。) view_args --> 将传递给视图函数的位置参数 view_kwargs -->将传递给视图函数的关键字参数 view_args和view_kwargs都不包含第一个视图参数(request)。 3. 返回值 返回None --> 继续往后执行,执行任何其他中间件的process_view方法,然后在执行相应的视图。 返回一个HttpResponse对象 --> Django不会调用适当的视图函数。 它将执行中间件的process_response方法并将应用到该HttpResponse并返回结果。
process_template_response(self,request,response)
process_template_response是在视图函数执行完成后立即执行,但是它有一个前提条件,那就是视图函数返回的对象有一个render()方法(或者表明该对象是一个TemplateResponse对象或等价方法)。 视图函数返回的响应对象有render方法的时候才执行
执行顺序是倒序
它的参数,一个HttpRequest对象,response是TemplateResponse对象(由视图函数或者中间件产生)。
process_exception(self, request, exception)
1. 视图函数中抛出异常的时候才执行 执行顺序也是按照中间件注册顺序的倒序执行。 只要有一个中间件中的process_exception 返回响应对象就不走了 直接执行 process_response方法 2. 参数 该方法两个参数: 一个HttpRequest对象 一个exception是视图函数异常产生的Exception对象。 3. 返回值 返回None --> 继续往后执行,交给下一个中间件的process_exception方法来处理异常 如果是HttpResponse对象,Django将调用模板和中间件中的process_response方法,并返回给浏览器,否则将默认处理异常。
视图函数抛出异常,视图函数有render方法执行

中间件版登录验证
AuthMD中间件注册后,所有的请求都要走AuthMD的process_request方法。
访问的URL在白名单内,则不做阻拦走正常流程;
如果URL在黑名单中,则返回This is an illegal URL的字符串;
正常的URL但是需要登录后访问,让浏览器跳转到登录页面。
注:AuthMD中间件中需要session,所以AuthMD注册的位置要在session中间的下方。
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse,redirect import time def ftime(stamp): strtime = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(stamp)) return strtime def check(time_list): now = time.time() temp=[] if time_list: for i in time_list: if float(i) + 60 > now: temp.append(i) time_list = temp if len(time_list) >= 3: print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))) print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(float(time_list[0]))),time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(float(time_list[1]))),time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(float(time_list[2])))) print(ftime(float(time_list[0])),ftime(float(time_list[1])),ftime(float(time_list[2]))) a = '已经访问三次了,最快需要{}秒才能访问'.format(float(time_list[0])+60-now) print(a) return HttpResponse(a) def check2(time_list): now = time.time() while time_list and float(time_list[-1]) - now > 60: time_list[-1].pop() if len(time_list) >=3: return HttpResponse('已经访问三次了,最快需要{}秒才能访问'.format(float(time_list[-1])+60-now)) class InnerMD(MiddlewareMixin): access = {} def process_request(self,request): ip = request.META.get('REMOTE_ADDR') if ip not in self.access: self.access[ip] = [] ret = check(self.access[ip]) if not ret: self.access[ip].insert(0,'{}'.format(time.time())) print('中间件InnerMD') else: return ret class AuthMD(MiddlewareMixin): white = [ '/index/','/login/', ] black = [ '/warn/'] def process_request(self,request): access_url = '{}'.format(request.path_info) if access_url in self.white: return None elif access_url in self.black: return HttpResponse('This is an illegal URL') else: if request.session.get('login',''): print('已登录') return None else: print('未登录,请进行登陆') next_url = request.path_info return redirect('/login/?next={}'.format(next_url))
Django请求流程图


浙公网安备 33010602011771号