Django框架-中间件
中间件
1、什么是Django的中间件?
定义:中间件是一个用来处理Django的请求和响应的框架级别的钩子,它是一个轻量、低级别的插件
系统,用于在全局范围内改变Django的输入和输出,每个中间件组件都负责一些特定的功能。
白话:中间件就是在视图函数执行前后做一些额外的操作,本质就是一个自定义的类,类中定义几个方法,用来在全局范围内处理请求和响应,在特定的时间去执行这些方法。
介于request与response处理之间的一道处理过程
Django的中间件执行顺序按settings.py中MIDDLEWARE注册索引从小到大执行
2、Django的默认的中间件
------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',
]
主要实现:
用户登录
日志记录
crsf:对所有的post请求做了一个验证,生成crst_token放在cookie中
session
权限管理
注意:对于所有请求的批量做处理的时候用中间件,单独对某几个函数做处理的时候用装饰器
3、定义5个方法
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)
4、方法的执行时间及执行顺序
4.1、process_request
1. 执行时间
在视图函数之前执行
2. 参数
request 和视图中的request是同一个
3. 返回值
返回None
返回response对象
不执行后面中间的process_request方法和视图
直接执行当前值中间件的process_response方法
4. 执行顺序
按照注册的顺序执行
4.2、process_response
1. 执行时间
在视图函数之后执行
2. request, response
request 和视图中的request是同一个
response 返回的response对象
3. 返回值
返回response对象
4. 执行顺序
按照注册的倒序执行
4.3、process_view
1. 执行时间
在视图函数之前,process_request之后执行
2. 参数
view_func 将要执行的视图函数
view_args 视图函数的可变长位置参数
view_kwargs 视图函数的可变长关键字参数
3. 返回值
返回 None 正常执行
返回 response对象 不执行后面的process_view和视图,直接执行所有中间件的process_response方法
4.执行顺序
按照注册的顺序执行
4.4、process_exception(有条件触发:有错误才执行)
1. 执行时间
在视图函数之后,process_response之前执行
2. 参数
exception 错误对象
3. 返回值
返回 None 不对错误进行处理,交给下一个中间件进行处理
返回 response对象 下一个中间的process_exception不执行,直接执行所有中间件的process_response方法
4. 执行顺序
按照注册的倒序执行
4.5、process_template_response(条件触发:视图返回的response有render方法)
1. 执行时间
在视图函数之后,process_response之前执行
2. 参数
3. 返回值
返回 response对象
4. 执行顺序
按照注册的倒序执行,执行完所有的process_template_response方法后执行response.render方法
6、中间件执行流程图
7、django请求的生命周期图
8、自定义中间件步骤
8.1、在应用app的根目录下创建一个文件夹midd_test,文件夹里创建一个py文件eg:midd.py
8.2、在midd.py中导入模块:from django.utils.deprecation import MiddlewareMixin
8.3、在midd.py中写类且必须继承MiddlewareMixin类,类里写几个方法
8.4、在项目setting.py中的MIDDLEWARE模块加上自定义的中间件路劲,
格式为:app_name.文件夹.py文件名.类名 eg: 'middlewares_app.midd_test.midd.Throttle',
9、中间件的方式解决登录
中间件版的登录验证需要依靠session,所以数据库种要有django_session表。
urlls.py
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^login/$, views.login , name='login'), url(r'^index/$', views.index, name='index'), url(r'^home/$', views.home, name='home'), ]
views.py
1 from django.shortcuts import render, HttpResponse, redirect 2 from app01 import models 3 4 5 def index(request): 6 return HttpResponse('this is index page') 7 8 9 def home(request): 10 return HttpResponse('this is home page') 11 12 13 def login(request): 14 if request.method == 'POST': 15 user = request.POST.get('user', '') 16 pwd = request.POST.get('pwd', '') 17 check = models.UserInfo.object.filter(username=user, password=pwd) 18 if check: 19 # 设置session 20 request.session['user'] = user 21 # 获取跳转登录页面之前的url 22 next_url = request.GET.get('next') 23 # 如果有,就跳转回登陆之前的url,否则默认跳转到index页面 24 if next_url: 25 return redirect(to=next_url) 26 else: 27 # return redirect('/index/') 28 from django.shortcuts import reverse 29 return redirect(reverse(viewname=index)) # url反向解析 30 return render(request, 'login.html')
login.html
1 {% load static %} 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <meta charset="UTF-8"> 6 {#<link rel="icon" href="/static/Bootstrap/imags/logo.jpg"> 下面的方式是静态文件动态#} 7 <link rel="icon" href="{% static 'Bootstrap/imags/logo.jpg' %}"> 8 <title>登陆页面</title> 9 </head> 10 <body> 11 <form action="{% url 'login' %}"> 12 {% csrf_token %} 13 <div> 14 <label for="user">用户名:</label> 15 <input type="text" id="user" name="user"> 16 </div> 17 <div> 18 <label for="pwd">用户名:</label> 19 <input type="password" id="pwd" name="pwd"> 20 </div> 21 <p><input type="submit" value="登录"></p> 22 </form> 23 </body> 24 </html>
mymidd.py
1 from django.utils.deprecation import MiddlewareMixin 2 3 4 class AuthMd(MiddlewareMixin): 5 white_list = ['/login/', ] # 白名单 6 black_list = ['/black/', ] # 黑名单 7 8 def process_request(self,request): 9 from django.shortcuts import redirect, HttpResponse 10 11 12 next_url = request.path_info 13 # 黑名单的网址限制访问 14 if next_url in self.black_list: 15 return HttpResponse('this is an illegal url') 16 elif next_url in self.white_list or request.session.get('user'): 17 return None 18 else: 19 return redirect('/login/?next={}'.format(next_url))
settings.py中注册自定义的中间件
1 MIDDLEWARE = [ 2 'django.middleware.security.SecurityMiddleware', 3 'django.contrib.sessions.middleware.SessionMiddleware', 4 'django.middleware.common.CommonMiddleware', 5 'django.middleware.csrf.CsrfViewMiddleware', 6 'django.contrib.auth.middleware.AuthenticationMiddleware', 7 'django.contrib.messages.middleware.MessageMiddleware', 8 'django.middleware.clickjacking.XFrameOptionsMiddleware', 9 'mymiddl.AuthMd' 10 ] 11 12 注册中间件
AuthMd中间件注册后,所有的请求都要走AuthMd的process_request方法。
如果url在黑名单中,则返回this is an illegal url;
访问的url在白名单内或者session中有urser用户名,则不做阻拦走正常流程,不需要登录就可以访问,如正常的注册,登录,主页;
其它正常的url都是需要登录后访问,让浏览器跳转到登录页面。
注:AuthMd中间件需要session,所以AuthMd注册的位置需要在session中间的下方。
10、中间件的方式做分流控制
1 # 存放用户数据 2 visit_list = { 3 # ip:[] 访问ip对应,每次访问时间 4 5 6 7 class Throttle(MiddlewareMixin): 8 """获取ip,记录时间,记录次数,根据访问记录做判断""" 9 10 def process_request(self, request): 11 print(request.META) 12 ip = request.META.get('REMOTE_ADDR') # 获取访问ip 13 now = time.time() # 记录当前时间,每次时间加入到最前面,即列表索引为0的位置 14 if not visit_list.get(ip, ''): # 首先判断当前访问ip是否在列表中 15 visit_list[ip] = [] # 不在的话,将访问ip加入到字典中,并创建value为空列表 16 # visit_list[ip].insert(0, now) 17 # 在的话,比较访问时间是否在10秒内,就可以访问,故删除之前的时间,将当前的时间写入 18 # 如果ip存在获取ip的访问时间记录列表 19 history = visit_list[ip] 20 print(history) 21 temp_list = [] # 解决循环删除列表问题 22 for i in history: 23 if now - i > 10: # 如果相距上次访问大于10秒,就将之前的清除[以防因记录3次导致符合要求的无法访问],将本次的时间写入 24 temp_list.append(i) # 加入到临时列表 25 else: 26 # 再如果ip存在且在10秒内再次访问,检查访问的次数 27 if len(history) >= 3: # 就不需要再加入时间了,直接返回警告 28 return HttpResponse('访问频率太快了,请等10秒后再来访问') 29 for j in temp_list: 30 history.remove(j) 31 history.insert(0, now) 32 33 34 ------------------------------------------------------------------------------------ 35 class Throttle(MiddlewareMixin): 36 """获取ip,记录时间,记录次数,根据访问记录做判断""" 37 38 def process_request(self, request): 39 ip = request.META.get('REMOTE_ADDR') 40 now = time.time() 41 if not visit_list.get(ip, ""): 42 visit_list[ip] = [] # 添加新用户,创建key:vulue 43 visit_list[ip].insert(0, now) # 每次都添加到第一个位置 44 else: 45 if now - visit_list[ip][0] > 10: # 超过10秒更新最新访问时间 46 visit_list[ip].clear() # 清空列表 47 visit_list[ip].insert(0, now) 48 else: # 未超过10秒,再来检测已访问的次数 49 if len(visit_list[ip]) >= 3: 50 return HttpResponse('too fast ,waiting a moment') 51 else: 52 visit_list[ip].insert(0, now) # 小于三次就把时间记录上 53 54 -------------------------------------------------------------------------------------- 55 visit_list = { 56 # 127.0.0.1:[] 57 } 58 59 class Throttle(MiddlewareMixin): 60 61 def process_request(self, request): 62 # 1. 63 # 获取ip 64 ip = request.META.get('REMOTE_ADDR') 65 66 if not visit_list.get(ip, ''): 67 visit_list[ip] = [] 68 history = visit_list[ip] 69 now = time.time() 70 # [ 10:21:10 ,10:21:06 ,10:21:05 ] 71 # 2. 记录时间 72 # 3. 根据访问记录做判断 73 while history and now - history[-1]>5: 74 history.pop() 75 76 if len(history) >= 3: 77 return HttpResponse('频率太快了') 78 history.insert(0,now)
1 -----midd.py # 在自己创建的py文件中写如下类 2 3 import time 4 from django.utils.deprecation import MiddlewareMixin 5 from django.shortcuts import HttpResponse 6 7 """ 8 # 需求:限制访问频率&限制用户10秒内访问同一页面最多3次,防止重复请求,增加服务器压力 9 思路: 10 1、获取访问用户的ip 11 2、记录每次用户访问该页面时的时间 12 3、比较用户每次访问时与前一次访问时间,如果超过10秒,可以继续访问,记录时间, 13 如果未超过,再看是否超过3次,如果超过次数,拒绝访问 14 4、设计存放用户访问记录的格式:{ip:[time1,time2,time3]} 15 5、每次过来通过ip获取上次的访问时间,做比较 16 """ 17 18 # 存放用户数据 19 visit_list = { 20 # ip:[] 访问ip对应,每次访问时间 21 } 22 23 class Throttle(MiddlewareMixin): 24 """获取ip,记录时间,记录次数,根据访问记录做判断""" 25 26 def process_request(self, request): 27 ip = request.META.get('REMOTE_ADDR') 28 now = time.time() 29 if not visit_list.get(ip, ""): 30 visit_list[ip] = [] # 添加新用户,创建key:vulue 31 else: 32 if now - visit_list[ip][0] > 10: # 超过10秒更新最新访问时间 33 visit_list[ip].clear() # 清空列表 34 else: # 未超过10秒,再来检测已访问的次数 35 if len(visit_list[ip]) >= 3: 36 return HttpResponse('too fast ,waiting a moment') 37 visit_list[ip].insert(0, now) # 小于三次就把时间记录上 , 每次都添加到第一个位置
上面的逻辑是错的,比如用户1,2,3秒时访问,属于10秒内3次访问,通过,第4秒时,小于10秒,但表中已经有3个了,不能访问,ok,
但用户第11秒时访问,这时过来10秒,应该能访问,但虽然与最后一次时间比小于10秒,但列表中已经有了3个时间,就导致不能访问了,
所以逻辑是错的,需要将最开始访问和现在时间比超过10秒的删除掉,保证列表中的时间永远是10秒内的。