Django框架(十二):中间件

下面是小凰凰的简介,看下吧!
💗人生态度:珍惜时间,渴望学习,热爱音乐,把握命运,享受生活
💗学习技能:网络 -> 云计算运维 -> python全栈( 当前正在学习中)
💗您的点赞、收藏、关注是对博主创作的最大鼓励,在此谢过!
有相关技能问题可以写在下方评论区,我们一起学习,一起进步。
后期会不断更新python全栈学习笔记,秉着质量博文为原则,写好每一篇博文。

先声明:中间件在各个领域都有不同的定义,不同的用法!这里讲解的只是django的中间件!

一、初识Django中间件

1、什么是中间件

中间件,它是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出。因为改变的是全局,所以需要谨慎实用,用不好会影响到性能。

2、中间件在Django框架中的使用

(1)Django框架

在这里插入图片描述
我们可以看到所有的请求以及回应消息都需要经过中间件,因此中间件有什么作用呢?其实就显而易见了,所有请求都要经过它,那么我们就可以使用中间件做些流量统计等一些全局性的操作。

(2)Django默认的中间件(setting.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',
]
(3)中间件中的方法

中间件中一共有四个方法:

process_request

process_view

process_exception

process_response

每个方法具体作用将会在自定义中间件中,结合实例进行介绍!

二、自定义中间件

在这里插入图片描述接下来我们就在Mymiddleware.py中写我们两个中间件类:Md1、Md2

自定义中间件:自己写一个类,但是必须继承MiddlewareMixin
需要导入:

from django.utils.deprecation import MiddlewareMixin

1、process_request、process_response讲解与实现

(1)讲解与实现
# views.py
def index(request):

    print("view函数...")
    return HttpResponse("OK")

不看注释部分,这就是process_request和process_response的正常状态,process_request应该return None,让请求可以一直往下走!process_response应该是把视图函数的响应返回,因此应该是return response!两个注释部分,是分别实现了对请求和响应的人为控制,详情请看下面!

# Mymiddlewares.py
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse

class Md1(MiddlewareMixin):

    def process_request(self,request):
        print("Md1请求")
 
    def process_response(self,request,response):
        print("Md1返回")
        return response
        # return HttpResponse("Md1把响应内容换了")

class Md2(MiddlewareMixin):

    def process_request(self,request):
        print("Md2请求")
        # return HttpResponse("Md2中断")
    def process_response(self,request,response):
        print("Md2返回")
        return response

结果:

Md1请求
Md2请求
view函数...
Md2返回
Md1返回

根据上面的结果我们可以得出,process_request、视图函数、process_response三者之间的执行流程:
在这里插入图片描述

(2)中间件的简单控制–对request的控制–拦截

如果当请求到达请求2的时候直接不符合条件返回,即return HttpResponse("Md2中断"),程序将把请求直接发给中间件2返回,然后依次返回到请求者,结果如下:

Md1请求
Md2请求
Md2返回
Md1返回

流程图如下:
在这里插入图片描述

(3)中间件的简单控制–对request的控制–白名单-放行
class Md1(MiddlewareMixin):

    def process_request(self,request):
        print("Md1请求")
        white_menu = ['/login']#白名单
        if   request.path in white_menu:
             # 如果访问的路径在白名单中,那么就放行(返回值是none)
             return None 
        else:
        	'''
        	不在白名单(一般就是需要登录才能访问的页面)
        	如果用户未登录的(没有sessionid或sessionid错误),
        	我们可以给它重定向到login页面
        	'''
 
    def process_response(self,request,response):
        print("Md1返回")
        return response
(4)中间件的简单控制–对response的控制–偷梁换柱

当响应到达Md1中间件时,执行process_response函数,return HttpResponse("Md1把响应内容换了"),用户收到的响应内容就是"Md1把响应内容换了",因此称之为偷梁换柱!

2、加入process_view讲解与实现

(1)了解process_view
process_view(self, request, callback, callback_args, callback_kwargs){
'''
	request: 请求对象
    callback: 将要执行的视图函数
    callback_args:将要执行的函数的位置参数
    callback_kwargs:将要执行的函数的关键字参数
'''
}
(2)process_view的应用

Mymiddlewares.py加入process_view函数:
注意:process_view一般没有返回值!

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse

class Md1(MiddlewareMixin):

    def process_request(self,request):
        print("Md1请求")
        #return HttpResponse("Md1中断")
    def process_response(self,request,response):
        print("Md1返回")
        return response

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print("Md1view")

class Md2(MiddlewareMixin):

    def process_request(self,request):
        print("Md2请求")
        return HttpResponse("Md2中断")
    def process_response(self,request,response):
        print("Md2返回")
        return response

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print("Md2view")

结果如下:

Md1请求
Md2请求
Md1view
Md2view
view函数...
Md2返回
Md1返回

下图进行分析上面的过程:
在这里插入图片描述
当最后一个中间的process_request到达路由关系映射之后,返回到中间件1的process_view,然后依次往下,到达views函数,最后通过process_response依次返回到达用户。

因为url路由之后,已经知道了自己将要执行的视图函数是啥,因此可以在中间件中提前执行。process_view可以用来调用视图函数:

class Md1(MiddlewareMixin):
        
    def process_view(self, request, callback, callback_args, callback_kwargs):

        response=callback(request,*callback_args,**callback_kwargs) # callback是将要执行的视图函数
        '''
        有位置参数就传*callback_args
        有关键字参数就传**callback_kwargs
        位置和关键字都有,那么*与**都要传
        没有参数就不要传*callback_args与**callback_kwargs
        '''
        return response

结果如下:

Md1请求
Md2请求
view函数...
Md2返回
Md1返回

注意:process_view如果有返回值会越过后面的process_view以及视图函数,但是所有的process_response都还会执行

3、再加入process_exception讲解与实现

(1)了解process_exception

顾名思义,这个方法是用来处理错误的。正常情况下,这个方法是不会被执行的。只有视图函数出现错误时才会执行

process_exception(self, request, exception)
(2)异常情况看流程–倒序执行

当views出现错误时:
在这里插入图片描述
一般情况下,视图函数出错就会出现大黄页面,我往视图函数中加了一个未定义的变量,前端报错渲染出来的样子如下:
在这里插入图片描述后台报错:
在这里插入图片描述
这种大黄页看起来实在是太难受了,因此我们可以利用process_exception,在视图函数出错时,渲染自己自定义的报错页面

接下来我们向Mymiddlewares.py加入process_exception函数:

class Md1(MiddlewareMixin):

    def process_request(self,request):
        print("Md1请求")

    def process_response(self,request,response):
        print("Md1返回")
        return response

    def process_view(self, request, callback, callback_args, callback_kwargs):

        print("md1 process_view...")

    def process_exception(self):
        print("md1 process_exception...")



class Md2(MiddlewareMixin):

    def process_request(self,request):
        print("Md2请求")

    def process_response(self,request,response):
        print("Md2返回")
        return response
    def process_view(self, request, callback, callback_args, callback_kwargs):
        print("md2 process_view...")

    def process_exception(self):
        print("md1 process_exception...")
        return HttpResponse("error")

请求结果如下:

Md1请求
Md2请求
md1 process_view...
md2 process_view...
view函数...
md2 process_exception...
Md2返回
Md1返回

当视图函数报错时,我们return HttpResponse("error")来代替大黄页面!你还可以自己写个报错页面,return render(...)

应用案例

1、做IP访问频率限制

某些IP访问服务器的频率过高,进行拦截,比如限制每分钟不能超过10次。
答案:

# 注意:成功时返回的是None,那样才会走视图层,返回httpresponse就直接出去了
import time
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
# 访问IP池
visit_ip_pool = {}
class RequestBlockingMiddleware(MiddlewareMixin):
    def process_request(self,request):
        # 获取访问者IP
        ip=request.META.get("REMOTE_ADDR")
        # 获取访问当前时间
        visit_time=time.time()
        # 判断如果访问IP不在池中,就将访问的ip时间插入到对应ip的key值列表,如{"127.0.0.1":[时间1]}
        if ip not in visit_ip_pool:
            visit_ip_pool[ip]=[visit_time]
            return None
        # 然后在从池中取出时间列表
        history_time = visit_ip_pool.get(ip)
        # 循环判断当前ip的时间列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
        while history_time and visit_time-history_time[-1]>60:
            history_time.pop()
        # 如果访问次数小于10次就将访问的ip时间插入到对应ip的key值列表的第一位置,如{"127.0.0.1":[时间2,时间1]}
        print(history_time)
        if len(history_time)<10:
            history_time.insert(0, visit_time)
            return None
        else:
            # 如果大于10次就禁止访问
            return HttpResponse("访问过于频繁,还需等待%s秒才能继续访问"%int(60-(visit_time-history_time[-1])))

2、URL访问过滤

如果用户访问的是login视图(放过)

如果访问其他视图,需要检测是不是有session认证,已经有了放行,没有就重定向到login,这样就省得在多个视图函数上写装饰器了!

答案:

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import redirect, reverse


class AuthMiddleware(MiddlewareMixin):

    def process_request(self, request):
        if request.path == '/login/':
            return None
        else:
            is_auth = request.user.is_authenticated
            if not is_auth:
                return redirect(reverse('login_view'))
©️2020 CSDN 皮肤主题: 鲸 设计师:meimeiellie 返回首页