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'),    
]
View Code

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')
View Code

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>
View Code

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 注册中间件
View Code

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秒内的。

 

posted @ 2018-10-03 15:50  Alive_2020  阅读(413)  评论(0编辑  收藏  举报