zoukankan      html  css  js  c++  java
  • 中间件

    中间件的五个方法

    中间件可以定义五个方法,分别是:(主要的是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)
    

    定义

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

    settings配置中

    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',
    ]
    配置的是一个个路径,封装一些功能,相当于
    from django.middleware.security import SecurityMiddleware
    

    登录验证

    条件:只有登录成功才能访问各个页面,也就是无论点击哪里都跳转到登录界面

    用session时会报一个错误

    装饰器版在cookie博客

    中间件版登录验证

    1.设置自定义中间件

    在应用app01中

    在应用app01中创建一个包,随便起名字,一般都放在一个叫做utils的包里面,表示一个公用的组件,创建一个py文件,随便起名字,例如叫做:middlewares.py,内容如下

    from django.utils.deprecation import MiddlewareMixin
    
    class LoginAuth(MiddlewareMixin):
        #请求来了,自动执行
        def process_request(self,request):
            print("请求来了")
    

    然后去settings中的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',
        'app01.utils.mymiddleware.LoginAuth'
    ]
    

    django的请求生命周期

    具体解释

    1. 浏览器请求,wsgi.py,它封装这socket,request对象,request对象给中间键,中间件根据middleware封装的中间件一层一层向下执行,自动执行每个中间件中的请求函数

    2. 执行顺序从上到下,例如,我们把自定义的中间件放在session中间件的上边,那么session就不起作用了,所以自定义的中间件是放在最下边位置

    3. 中间件加工完之后,给了url控制器,然后执行视图函数views,视图函数再通过orm与数据库进行交互,并且渲染页面

      注意如果此处没有return值,默认返回的是None,表示正常,向下执行,如果return值是别的值,那么就不会向下执行了,直接执行响应中间件的response方法给浏览器了

    4. 执行完成后,页面需要间数据返还给浏览器,不需要经过url,但是还会经过中间件

    5. 中间件中执行process_response方法

    所以由上引入白名单概念,就是让不需要session的函数先执行,例如login函数

    白名单

    中间件的py文件中

    from django.utils.deprecation import MiddlewareMixin
    from django.shortcuts import render,HttpResponse,redirect
    class LoginAuth(MiddlewareMixin):
    
        white_list = ["/login/"]
        def process_request(self,request):
            print("请求来啦")
            path = request.path
            if path not in self.white_list:
                is_login = request.session.get("is_login")
                if not is_login:
                    return redirect(f"/login/?requestUrl={path}")  # 路径拼接,确定跳转前的路径
    
        def process_response(self,request,response):
            print("请求走了")
            return response # 必须返回response对象
    

    views.py 应用了中间件就不用了装饰器了

    from django.shortcuts import render,HttpResponse,redirect
    def login(request):
        print("这是login函数")
    
        if request.method == "GET":
            return render(request, "login.html")
        else:
            username = request.POST.get("username")
            password = request.POST.get("password")
            if username == "alex" and password == "123":
                request.session["is_login"] = "yes"
                requestUrl = request.GET.get("requestUrl")
                return redirect(requestUrl if requestUrl else "home")
            else:
                return redirect("login")
    
    def home(request):
        print("这是home函数")
        return render(request, "home.html",{"user":"woshishui"})
    
    def index(request):
        print("这是index函数")
        return render(request, "index.html")
    

    在上述代码中,请求白名单中的数据时,根据请求路径判断下列条件的结果,当路径在白名单中的时候,if条件不成立,默认返回None,正常执行views函数,如果访问home函数等,以为没有在白名单中,执行if条件,如果已经有了session值,则默认返回None,正常执行函数,如果没有session,则执行下一个if判断,重定向到login路径,再次请求,执行同上

    中间件有多个方法时的执行顺序

    settings中

    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',
        # 'app01.utils.mymiddleware.LoginAuth'
        'app01.utils.mymiddleware.MD1',  # 新增中间件
        "app01.utils.mymiddleware.MD2",  # 新增中加件
    ]
    

    views.py

    from django.shortcuts import render,HttpResponse,redirect
    
    # Create your views here.
    
    def home(request):
        print("这是home函数")
        return render(request,"home.html")
    
    def login(request):
        if request.method == "GET":
            print("这是login函数")
            return render(request,"login.html")
        else:
            user = request.POST.get("username")
            pwd = request.POST.get("password")
            if user == "alex" and pwd == "123":
                request.session["is_login"] = True
                return redirect("home")
            else:
                return redirect("login")
    
    def index(request):
        print("这是index函数")
        return render(request,"index.html")
    

    process_resquest 和 process_response 方法顺序

    middleware.py 中间件中文件

    class MD1(MiddlewareMixin):
        def process_request(self,request):
            #request是一个请求的对象
            print("MD1的process_request方法",request)
            #return HttpResponse("ok")   ---------------------- 第一知识点  第一执行
    
        def process_response(self,request,response):
            print("MD1的process_response方法",response)
            return response
        	#return	HttpResponse("ok")  ---------------------- 第二知识点  第四执行
    
    class MD2(MiddlewareMixin):
        def process_request(self,request):
            print("MD2的process_request方法",request)
            #return HttpResponse("ok")  ----------------------- 第三知识点   第二执行
    
        def process_response(self,request,response):
            print("MD2的process_response方法",response)
            #response 接收的是函数的返回值,是一个response对象
            return response
        	#return HttpResponse("ok")  ---------------------- 第四知识点    第三执行
    

    有return自定义内容的时候

    都是get方法时验证

    第一知识点解释

    MD1的process_request方法 <WSGIRequest: GET '/index/'>
    MD1的process_response方法 <HttpResponse status_code=200, "text/html; charset=utf-8">
    当我们在浏览器发送请求时,执行到中间件时就不再向后执行,只执行返回函数process_response
    

    第二知识点解释

    MD1的process_request方法 <WSGIRequest: GET '/index/'>
    MD2的process_request方法 <WSGIRequest: GET '/index/'>
    这是index函数
    MD2的process_response方法 <HttpResponse status_code=200, "text/html; charset=utf-8">
    MD1的process_response方法 <HttpResponse status_code=200, "text/html; charset=utf-8">
    当我们在浏览器发送请求时,由于返回值的位置是最后执行的,所以所有的函数都执行了,但是由于返回的是自定义的,而不是response对象,所以返回页面不是html文件,而是ok
    

    第三知识点解释

    MD1的process_request方法 <WSGIRequest: GET '/login/'>
    MD2的process_request方法 <WSGIRequest: GET '/login/'>
    MD2的process_response方法 <HttpResponse status_code=200, "text/html; charset=utf-8">
    MD1的process_response方法 <HttpResponse status_code=200, "text/html; charset=utf-8">
    当我们在浏览器发送请求时,执行到中间件时就不再向后执行,只执行返回函数process_response,依次向上执行(中间件顺序) 所以返回页面不是html文件,而是ok
    

    第四知识点解释

    MD1的process_request方法 <WSGIRequest: GET '/login/'>
    MD2的process_request方法 <WSGIRequest: GET '/login/'>
    这是login函数
    MD2的process_response方法 <HttpResponse status_code=200, "text/html; charset=utf-8">
    MD1的process_response方法 <HttpResponse status_code=200, "text/html; charset=utf-8">
    当我们在浏览器发送请求时,由于返回值的位置是最后执行的,所以所有的函数都执行了,但是由于返回的是自定义的,而不是response对象,所以返回页面不是html文件,而是ok
    

    process_view

    middleware.py

    class MD1(MiddlewareMixin):
        def process_request(self,request):
            #request是一个请求的对象
            print("MD1的process_request方法",request)
            # return HttpResponse("ok")
    
        def process_response(self,request,response):
            print("MD1的process_response方法",response)
            return response
            # return HttpResponse("ok")
    
        def process_view(self,request,view_func,view_args,view_kwargs):
            print(view_func)  # 是个视图函数,也就是执行此函数之前可以得到路由路径
            print("MD1的process_view")
    
    class MD2(MiddlewareMixin):
        def process_request(self,request):
            print("MD2的process_request方法",request)
            # return HttpResponse("ok")
    
        def process_response(self,request,response):
            print("MD2的process_response方法",response)
            #response 接收的是函数的返回值,是一个response对象
            return response
            # return HttpResponse("ok")
    
        def process_view(self,request,view_func,view_args,view_kwargs):
            print(view_func)
            print("MD2的process_view")
    

    index请求结果

    MD1的process_request方法 <WSGIRequest: GET '/login/'>
    MD2的process_request方法 <WSGIRequest: GET '/login/'>
    <function login at 0x0000002B48EDA2F0>
    MD1的process_view
    <function login at 0x0000002B48EDA2F0>
    MD2的process_view
    这是login函数
    MD2的process_response方法 <HttpResponse status_code=200, "text/html; charset=utf-8">
    MD1的process_response方法 <HttpResponse status_code=200, "text/html; charset=utf-8">
    

    执行顺序图

    process_exception

    这个比较特殊,只有在有报错的情况下才执行,否则不执行

    middleware.py

    class MD1(MiddlewareMixin):
        def process_request(self,request):
            #request是一个请求的对象
            print("MD1的process_request方法",request)
            # return HttpResponse("ok")
    
        def process_response(self,request,response):
            print("MD1的process_response方法",response)
            return response
            # return HttpResponse("ok")
    
        def process_view(self,request,view_func,view_args,view_kwargs):
            print(view_func)
            print("MD1的process_view")
    
        def process_exception(self, request, exception):
            print(exception)
            print("MD1的process_exception")
    
    class MD2(MiddlewareMixin):
        def process_request(self,request):
            print("MD2的process_request方法",request)
            # return HttpResponse("ok")
    
        def process_response(self,request,response):
            print("MD2的process_response方法",response)
            #response 接收的是函数的返回值,是一个response对象
            return response
            # return HttpResponse("ok")
    
        def process_view(self,request,view_func,view_args,view_kwargs):
            print(view_func)  
            print("MD2的process_view")
    
        def process_exception(self, request, exception):
            print(exception)    #打印的是抛出的错误信息
            print("MD2的process_exception")
    

    为了报错修改views.py代码

    def home(request):
        print("这是home函数")
        return render(request,"home.html")
    
    def login(request):
        if request.method == "GET":
            print("这是login函数")
            raise ValueError("抛出错误")   #------注意此处要抛错才能执行exception
            return render(request,"login.html")
        else:
            user = request.POST.get("username")
            pwd = request.POST.get("password")
            if user == "alex" and pwd == "123":
                request.session["is_login"] = True
                return redirect("home")
            else:
                return redirect("login")
    
    def index(request):
        print("这是index函数")
        return render(request,"index.html")
    

    执行结果

    MD1的process_request方法 <WSGIRequest: GET '/login/'>
    MD2的process_request方法 <WSGIRequest: GET '/login/'>
    <function login at 0x0000000AD8F5A268>
    MD1的process_view
    <function login at 0x0000000AD8F5A268>
    MD2的process_view
    这是login函数
    抛出错误
    MD2的process_exception
    抛出错误
    MD1的process_exception
    Internal Server Error: /login/
    Traceback (most recent call last):
      File "C:Python36libsite-packagesdjangocorehandlersexception.py", line 41, in inner
        response = get_response(request)
      File "C:Python36libsite-packagesdjangocorehandlersase.py", line 187, in _get_response
        response = self.process_exception_by_middleware(e, request)
      File "C:Python36libsite-packagesdjangocorehandlersase.py", line 185, in _get_response
        response = wrapped_callback(request, *callback_args, **callback_kwargs)
      File "D:Django项目day67zhongjianjianapp01views.py", line 23, in login
        raise ValueError("抛出错误")
    ValueError: 抛出错误
    [16/Oct/2019 17:25:55] "GET /login/ HTTP/1.1" 500 65127
    MD2的process_response方法 <HttpResponse status_code=500, "text/html">
    MD1的process_response方法 <HttpResponse status_code=500, "text/html">
    
    

    process_template_response

    中间件文件写法

    from django.utils.deprecation import MiddlewareMixin
    from django.middleware.csrf import MiddlewareMixin
    class MD1(MiddlewareMixin):
        def process_template_response(self, request, response):
            print(response.template_name)  # 后端返回的html文件index.html
            print(response.context_data)   # 后端返回装饰的键值对{"name":"dsb"}
    
            response.template_name = "home.html"
            response.context_data["user"] = "gunduzi" # 原来有就修改,没有就增加
            print(response.template_name)  # 后端返回的html文件home.html
            print(response.context_data)  # 后端返回装饰的键值对{'name': 'dsb', 'user': 'gunduzi'}
    
            print("MD2 process_template_response")
            return response
    

    views.py

    from django.shortcuts import render,HttpResponse,redirect
    from django.template.response import TemplateResponse
    def index(request):
        print("这是index函数")
        # return render(request, "index.html")
        return TemplateResponse(request, "index.html",{"name":"dsb"}) 
    
    # 必须用TemplateResponse,不能用render
    

    另一种执行process_template_response的方法,了解,不推荐

    def index(request):
        print("app01 中的 index视图")
      #raise ValueError('出错啦') 
        def render():
            print("in index/render")  
            #raise ValueError('出错啦') #至于render函数中报错了,那么会先执行process_template_response方法,然后执行process_exception方法,如果是在render方法外面报错了,那么就不会执行这个process_template_response方法了。
            return HttpResponse("O98K") #返回的将是这个新的对象
        rep = HttpResponse("OK")
        rep.render = render
        return rep
    

    中间件限制访问频率

    相关字典知识

    dic = {}
    # 有就获得值,没有为None,
    # 没有时可以给添加默认结果
    print(dic.get("ip",[]))
    
    dic = {"ip":[1,2,3]}
    # 删除具有返回值
    print(dic["ip"].pop(0))
    
    lst = []
    for i in lst:
        print(i) # 什么都没有
    

    题目:5秒时间内最多访问3次

    from django.utils.deprecation import MiddlewareMixin
    from django.shortcuts import HttpResponse, redirect, render
    import time
    
    visit_history = {}
    

    第一种方法

    class LoginAuth(MiddlewareMixin):
    
        def process_request(self, request):
            # 获取当前用户的IP
            ip = request.META.get('REMOTE_ADDR')
            # print(ip)
            # 获取当前的时间
            now = time.time()
            # 用户没有访问过时将其访问的时间点设为列表中的键
            if ip not in visit_history:
                visit_history[ip] = [now]
                print(visit_history)
                return
            # 用户第四次访问时字典的键已经有3个值了,走这个条件
            elif len(visit_history[ip]) == 3:
                # 删除掉第一个时间点,并将第四个时间点添加进去
                # pop具有返回值
                s1 = visit_history[ip].pop(0)
                visit_history[ip].append(now)
                print(visit_history)
                # 判断从第一次到第四次时间是否超过5s
                if now - s1 <= 5:
                    return HttpResponse('访问过于频繁')
            else:
                visit_history[ip].append(now)
                print(visit_history)
    

    第二种方法

    class LoginAuth(MiddlewareMixin):
    # 第一次访问,history为空,第二次有1个值,第三次2个值,第四次3个值
    # 只有第四次也在5s时间内时lst才会保持3个值,否则就达不到3个,就能正常访问
    
        def process_request(self, request):
            # 获取当前用户的IP
            ip = request.META.get('REMOTE_ADDR')
            # ip不在字典时结果为[]
            history = visit_history.get(ip, [])
            # print(ip)
            # 获取当前的时间
            now = time.time()
            # 用户没有访问过时将其访问的时间点设为列表中的键
            # 每次访问lst都是新的,是空列表
            lst = []
            # 第一次访问,什么都没有,走最后两句
            for i in history:
                if now - i < 5:
                    # i是5秒内的时间 需要统计
                    lst.append(i)
            print(lst)
    
            if len(lst) >= 3:
                # print(lst)
                return HttpResponse('频率太快,先歇一会')
    
            lst.append(now)
            # print(lst)
            # 给字典添加键值对
            visit_history[ip] = lst
    

    第三种方法

    class LoginAuth(MiddlewareMixin):
    # 第一次访问,history为空,第二次有1个值,第三次2个值,第四次3个值
    
        def process_request(self, request):
            # 获取当前用户的IP
            ip = request.META.get('REMOTE_ADDR')
            # ip不在字典时结果为[]
            history = visit_history.get(ip, [])
            # print(ip)
            # 获取当前的时间
            now = time.time()
            # history不为空并且新值和最久的值时间差大于5秒,删除旧值
            # 如果已有3个值,并且第4个值还在5秒内,那就不会执行while
            while history and now - history[-1] > 5:
                history.pop()
    
            if len(history) >= 3:
                return HttpResponse('频率太快,先歇一会')
            # 注意新值插入的顺序,要在最后
            history.insert(0,now)
            visit_history[ip] = history
    
  • 相关阅读:
    BZOJ2199[Usaco2011 Jan]奶牛议会——2-SAT+tarjan缩点
    BZOJ3862Little Devil I——树链剖分+线段树
    BZOJ2325[ZJOI2011]道馆之战——树链剖分+线段树
    BZOJ1018[SHOI2008]堵塞的交通——线段树
    BZOJ2733[HNOI2012]永无乡——线段树合并+并查集+启发式合并
    BZOJ4127Abs——树链剖分+线段树
    bzoj 4753 最佳团体
    bzoj 4472 salesman
    bzoj 5369 最大前缀和
    bzoj 1226 学校食堂Dining
  • 原文地址:https://www.cnblogs.com/lvweihe/p/11807031.html
Copyright © 2011-2022 走看看