Django之中间件

中间件

1,默认中间件

  • 中间件:Django中settings.py文件中的配置项,是Django默认自带的一些中间件

  • 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',
    ]
    
  • ​ MIDDLEWARE配置项是一个列表,列表中是一个个字符串,这些字符创其实是一个个类,也就是一个个中间件

2,中间件的五个方法

  • process_request(self,request)
    (没有返回值,即返回值是None)
    (如果有返回值,则会直接返回浏览器)
    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)
    (必须有返回response)
    
  • 以上方法的返回值可以是一个None或者一个HTTPResponse对象,如果是None,则继续按照Django定义的规则向后继续执行,如果是HTTPresponse对象,则直接将该对象返回给用户

  • 当用户发起请求时,会依次经过所有的中间件,这个时候的请求process_request,最后到达视图函数views中,views处理后,在依次穿过中间件,这个时候是process_response,最后返回请求者

  • img

1,process_request

  •  process_request有一个参数,就是request,这个request和视图函数中的request是一样的。
  • 它的返回值可以是None也可以是HttpResponse对象。返回值是None的话,按正常流程继续走,交给下一个中间件处理,如果是HttpResponse对象,Django将不执行视图函数,而将相应对象返回给浏览器。

2,process_response

  • 它有两个参数,一个是request,一个是response,request就是上述例子中一样的对象,response是视图函数返回的HttpResponse对象。该方法的返回值也必须是HttpResponse对象。

  • 必须返回response,不然你上层的中间件就没有拿到httpresponse对象,就会报错
    

3,process_view

  • process_view(self, request, view_func, view_args, view_kwargs)
  • 该方法有四个参数,Django会在调用视图函数之前调用process_view方法
    • request是HttpRequest对象。
    • view_func是Django即将使用的视图函数。 (它是实际的函数对象,而不是函数的名称作为字符串。)
    • view_args是将传递给视图的位置参数的列表.
    • view_kwargs是将传递给视图的关键字参数的字典。 view_args和view_kwargs都不包含第一个视图参数(request)。
  • 它应该返回None或一个HttpResponse对象。 如果返回None,Django将继续处理这个请求,执行任何其他中间件的process_view方法,然后在执行相应的视图。 如果它返回一个HttpResponse对象,Django不会调用对应的视图函数。 它将执行中间件的process_response方法并将应用到该HttpResponse并返回结果。

img

4,process_exception

  • process_exception(self, request, exception)
  • 该方法两个参数:
    • 一个HttpRequest对象
    • 一个exception是视图函数异常产生的Exception对象。
  • 这个方法只有在视图函数中出现异常了才执行,它返回的值可以是一个None也可以是一个HttpResponse对象。如果是HttpResponse对象,Django将调用模板和中间件中的process_response方法,并返回给浏览器,否则将默认处理异常。如果返回一个None,则交给下一个中间件的process_exception方法来处理异常。它的执行顺序也是按照中间件注册顺序的倒序执行。
  • 如果视图函数中无异常,process_exception方法不执行。
  • img

5,process_template_response(用的比较少)

  • process_template_response(self, request, response)

          它的参数,一个HttpRequest对象,response是TemplateResponse对象(由视图函数或者中间件产生)。

          process_template_response是在视图函数执行完成后立即执行,但是它有一个前提条件,那就是视图函数返回的对象有一个render()方法(或者表明该对象是一个TemplateResponse对象或等价方法)。

  • 视图函数执行完之后,立即执行了中间件的process_template_response方法,顺序是倒序,先执行MD2的,在执行MD1的,接着执行了视图函数返回的HttpResponse对象的render方法,返回了一个新的HttpResponse对象,接着执行中间件的process_response方法。

3,自定义中间件

  • 自己写一个类,必须继承MiddlewareMixin

  • 步骤:

    • 在项目的应用文件夹下创建一个包,名字:例如:mymiddleware

    • 在包中创建一个py文件:例如:mymiddelware

    • mymiddleware.py

      • from diango.utils.deprecation import MiddlewareMixin
        
        class Md1(MiddlewareMixin):
        	def process_request(self, request):
        		print("Md1中的process_request")
        		
        	def process_reponse(self, request, response):
        		print("Md1中的process_response")
        		return response
        		
        		
        class Md2(MiddlewareMixin):
        	def process_request(self, request):
        		print("Md2中的process_request")
        		
        	def process_reponse(self, request, response):
        		print("Md2中的process_response")
        		return response
        		
        执行顺序:
        Md1请求
        Md2请求
        view函数...
        Md2返回
        Md1返回
        
  • 多个中间件中的process_response方法是按照MIDDLEWARE中的注册顺序倒序执行的,也就是说第一个中间件的process_request方法首先执行,而它的process_response方法最后执行,最后一个中间件的process_request方法最后一个执行,它的process_response方法是最先执行

img

  • 自定义中间件应用

    • session登录认证,

      • class M1(MiddlewareMixin):
        
            def process_request(self,request):
        
                #设置路径白名单,只要访问的是login登陆路径,就不做这个cookie认证
                if request.path not in [reverse('login'),]:
                    print('我是M1中间件') #客户端IP地址
                    # return HttpResponse('sorry,没有通过我的M1中间件')
                    is_login = request.COOKIES.get('is_login', False)
        
                    if is_login:
                        pass
                    else:
                        # return render(request,'login.html')
                        return redirect(reverse('login'))
                else:
                    return None #别忘了return None,或者直接写个pass
        
            def process_response(self,request,response):
        
                print('M1响应部分')
                # print(response.__dict__['_container'][0].decode('utf-8'))
                return response
                # return HttpResponse('瞎搞')
        
    • ip访问频率限制:某些ip访问服务器频率过高,进行拦截

    • url访问过滤:

      • 如果用户访问的是login视图,放过,即将login加入白名单
      • 如果访问其他视图,需要检测是不是有session认证,已经有了放行,没有返回login,这样就省的在每个视图函数上面写装饰器了

4,Django生命周期

img

5,访问限制

  • 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])))
    
希望你眼眸有星辰,心中有山海,从此以梦为马,不负韶华
原文地址:https://www.cnblogs.com/daviddd/p/12049621.html