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

    一、中间件的概念

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

    Django的中间件的定义:

    Middleware is a framework of hooks into Django’s request/response processing. <br>It’s a light, low-level “plugin” system for globally altering Django’s input or output.
    

      如果想修改请求,例如被传送到view中的HttpRequest对象。 或者想修改view返回的HttpResponse对象,这些都可以通过中间件来实现。

      可能还想在view执行之前做一些操作,这种情况就可以用 middleware来实现。

    Django默认的Middleware可以在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',
    ]
    

      MIDDLEWARE配置项是一个列表,列表中是一个个字符串,这些字符串其实是一个个类,也就是一个个中间件。

      我们之前已经接触过一个csrf相关的中间件了,之前将这一行注释,再提交post请求的时候,就不会被forbidden了,后来学会使用csrf_token就不用再注释这个中间件了。

      手动引入一个中间件:

    from django.middleware.security import SecurityMiddleware
    

      查看中间件源码:

    import re
    
    from django.conf import settings
    from django.http import HttpResponsePermanentRedirect
    from django.utils.deprecation import MiddlewareMixin
    
    
    class SecurityMiddleware(MiddlewareMixin):
        def __init__(self, get_response=None):
            self.sts_seconds = settings.SECURE_HSTS_SECONDS
            self.sts_include_subdomains = settings.SECURE_HSTS_INCLUDE_SUBDOMAINS
            self.sts_preload = settings.SECURE_HSTS_PRELOAD
            self.content_type_nosniff = settings.SECURE_CONTENT_TYPE_NOSNIFF
            self.xss_filter = settings.SECURE_BROWSER_XSS_FILTER
            self.redirect = settings.SECURE_SSL_REDIRECT
            self.redirect_host = settings.SECURE_SSL_HOST
            self.redirect_exempt = [re.compile(r) for r in settings.SECURE_REDIRECT_EXEMPT]
            self.get_response = get_response
    
        def process_request(self, request):
            path = request.path.lstrip("/")
            if (self.redirect and not request.is_secure() and
                    not any(pattern.search(path)
                            for pattern in self.redirect_exempt)):
                host = self.redirect_host or request.get_host()
                return HttpResponsePermanentRedirect(
                    "https://%s%s" % (host, request.get_full_path())
                )
    
        def process_response(self, request, response):
            if (self.sts_seconds and request.is_secure() and
                    'strict-transport-security' not in response):
                sts_header = "max-age=%s" % self.sts_seconds
                if self.sts_include_subdomains:
                    sts_header = sts_header + "; includeSubDomains"
                if self.sts_preload:
                    sts_header = sts_header + "; preload"
                response["strict-transport-security"] = sts_header
    
            if self.content_type_nosniff and 'x-content-type-options' not in response:
                response["x-content-type-options"] = "nosniff"
    
            if self.xss_filter and 'x-xss-protection' not in response:
                response["x-xss-protection"] = "1; mode=block"
    
            return response
    SecurityMiddleware源码

      每个中间件都有具体的功能。

    二、自定义中间件

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

      以上方法的返回值可以是None或一个HttpResponse对象,如果是None,则继续按照django定义的规则向后继续执行,如果是HttpResponse对象,则直接将该对象返回给用户。

    1、process_request, process_response

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

      上述截图中的中间件都是django中的,我们也可以自己定义一个中间件,自己写一个类,但是必须继承MiddlewareMixin。

      自定义中间件需要引入:

    from django.utils.deprecation import MiddlewareMixin
    

      

    在视图函数中:

    def index(request):
        print("index.....")
        return HttpResponse("Index")

    在自定义中间件my_middlewares.py中:

    from django.utils.deprecation import MiddlewareMixin
    from django.shortcuts import HttpResponse
    
    class CustomerMiddleware(MiddlewareMixin):
    
        def process_request(self, request):
            print("CustomMiddleware process_request...")
    
        def process_response(self, request, response):
            # 必须有返回值,要一层层往回传,不加就会报错
            print("CustomMiddleware process_response")
            return response
    
    class CustomerMiddleware2(MiddlewareMixin):
    
        def process_request(self, request):
            print("CustomMiddleware2 process_request...")
    
        def process_response(self, request, response):
            print("CustomMiddleware2 process_response")
            return response

     访问index页面,控制台输出结果:

    CustomMiddleware process_request...
    CustomMiddleware2 process_request...
    index.....
    CustomMiddleware2 process_response
    CustomMiddleware process_response
    

      注意:1 .在process_request函数中,如果添加了return语句,会发生中断现象,程序把请求发给中间件,然后依次返回给请求者。

         2.在process_response函数中,一定要有return语句,因为需要一层层往回传值给请求者(浏览器)。

    (1)如果是在CustomerMiddleware类中的process_request函数中添加return  HttpResponse("forbidden..."),输出如下:

    网页显示:
    forbidden...
    
    控制台输出:
    CustomMiddleware process_request...
    CustomMiddleware process_response
    

    (2)如果是在CustomerMiddleware2类中的process_request函数中添加return  HttpResponse("forbidden..."),输出如下:

    网页显示:
    forbidden...
    
    控制台输出
    CustomMiddleware process_request...
    CustomMiddleware2 process_request...
    CustomMiddleware2 process_response
    CustomMiddleware process_response

     流程图如下:

      

    2、process_view

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

      my_middlewares.py修改如下:

    from django.utils.deprecation import MiddlewareMixin
    from django.shortcuts import HttpResponse
    
    class CustomerMiddleware(MiddlewareMixin):
    
        def process_request(self, request):
            print("CustomMiddleware process_request...")
    
        def process_response(self, request, response):
            # 必须有返回值,要一层层往回传,不加就会报错
            print("CustomMiddleware process_response")
            return response
    
        def process_view(self, request, callback, callback_args, callback_kwargs):
            print("CustomMiddleware1  process_view")
    
    class CustomerMiddleware2(MiddlewareMixin):
    
        def process_request(self, request):
            print("CustomMiddleware2 process_request...")
    
        def process_response(self, request, response):
            print("CustomMiddleware2 process_response")
            return response
    
        def process_view(self, request, callback, callback_args, callback_kwargs):
            print("CustomMiddleware2  process_view")
    

      执行结果如下所示:

    网页显示:
    index
    
    控制台输出
    CustomMiddleware process_request...
    CustomMiddleware2 process_request...
    CustomMiddleware1  process_view
    CustomMiddleware2  process_view
    index.....
    CustomMiddleware2 process_response
    CustomMiddleware process_response
    

      上述代码运行过程见下图:

      

      当最后一个中间件的process_request到达路由关系映射之后,返回到中间件1的process_view,然后依次往下,到达views函数,最后通过process_response依次返回到达用户。

      意义:在中间件走完时,多走了一步路由控制,而callback参数就是用来找到这次请求对应的视图函数(callback_args是视图函数的参数),因此可以提前一步来执行视图函数,而如果return直接返回,则可以跳过视图函数这一步直接返回。

    示例1:用process_view来调用视图函数

      仅修改CustomMiddleware2的process_view函数,修改如下:

    class CustomerMiddleware2(MiddlewareMixin):
    
        def process_request(self, request):
            print("CustomMiddleware2 process_request...")
            # return HttpResponse("forbidden...")
    
        def process_response(self, request, response):
            print("CustomMiddleware2 process_response")
            return response
    
        def process_view(self, request, callback, callback_args, callback_kwargs):
            print("====》", callback(callback_args))
            print("CustomMiddleware2  process_view")
    

      输出如下所示:

    CustomMiddleware process_request...
    CustomMiddleware2 process_request...
    CustomMiddleware1  process_view
    index.....
    ====》 <HttpResponse status_code=200, "text/html; charset=utf-8">
    CustomMiddleware2  process_view
    index.....
    CustomMiddleware2 process_response
    CustomMiddleware process_response

     示例2:给process_view函数返回值

      仅修改CustomMiddleware2的process_view函数,修改如下:

    class CustomerMiddleware2(MiddlewareMixin):
    
        def process_request(self, request):
            print("CustomMiddleware2 process_request...")
            # return HttpResponse("forbidden...")
    
        def process_response(self, request, response):
            print("CustomMiddleware2 process_response")
            return response
    
        def process_view(self, request, callback, callback_args, callback_kwargs):
            # print("====》", callback(callback_args))
            print("CustomMiddleware2  process_view")
            return HttpResponse("123")
    

    输出如下所示:

    页面显示:
    123
    
    控制台输出:
    CustomMiddleware process_request...
    CustomMiddleware2 process_request...
    CustomMiddleware1  process_view
    CustomMiddleware2  process_view
    CustomMiddleware2 process_response
    CustomMiddleware process_response
    

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

    3、process_exception 

    def process_exception(self, request, exception):...
    

      示例修改如下:

    class CustomerMiddleware(MiddlewareMixin):
    
        def process_request(self, request):
            print("CustomMiddleware1 process_request...")
    
        def process_response(self, request, response):
            # 必须有返回值,要一层层往回传,不加就会报错
            print("CustomMiddleware1 process_response")
            return response
    
        def process_view(self, request, callback, callback_args, callback_kwargs):
            print("CustomMiddleware1  process_view")
    
        def process_exception(self, request, exception):
            print("CustomMiddleware1 process_exception")
    
    
    class CustomerMiddleware2(MiddlewareMixin):
    
        def process_request(self, request):
            print("CustomMiddleware2 process_request...")
    
        def process_response(self, request, response):
            print("CustomMiddleware2 process_response")
            return response
    
        def process_view(self, request, callback, callback_args, callback_kwargs):
            print("CustomMiddleware2  process_view")
    
        def process_exception(self, request, exception):
            print("CustomMiddleware2 process_exception")
    

      输出如下所示:

    网页显示:
    Index
    
    控制台输出:
    CustomMiddleware1 process_request...
    CustomMiddleware2 process_request...
    CustomMiddleware1  process_view
    CustomMiddleware2  process_view
    index.....
    CustomMiddleware2 process_response
    CustomMiddleware1 process_response
    

      注意:在代码正常情况下不会执行,当视图中报错时,才会依次执行process_excepsion和process_response。

      当views出现错误时流程图如下所示:

      

    (1)在视图函数中填写错误代码

    def index(request):
        print("index.....")
        yuan
        return HttpResponse("Index")
    

      输出如下:

      1)页面显示错误提示

      2)控制台输出

    CustomMiddleware1 process_request...
    CustomMiddleware2 process_request...
    CustomMiddleware1  process_view
    CustomMiddleware2  process_view
    Internal Server Error: /index/
    index.....
    CustomMiddleware2 process_exception
    CustomMiddleware1 process_exception
    大段的错误提示
    CustomMiddleware2 process_response
    CustomMiddleware1 process_response
    

    (2)在process_exception中抓取异常信息,返回到页面中显示

      仅对CustomerMiddleware2中的process_exception修改:

    class CustomerMiddleware2(MiddlewareMixin):
    
        def process_request(self, request):
            print("CustomMiddleware2 process_request...")
            # return HttpResponse("forbidden...")
    
        def process_response(self, request, response):
            print("CustomMiddleware2 process_response")
            return response
    
        def process_view(self, request, callback, callback_args, callback_kwargs):
            print("CustomMiddleware2  process_view")
    
        def process_exception(self, request, exception):
            print("CustomMiddleware2 process_exception")
            return HttpResponse(exception)
    

      输出如下:

    页面显示:
    name 'yuan' is not defined
    
    控制台输出:
    CustomMiddleware1 process_request...
    CustomMiddleware2 process_request...
    CustomMiddleware1  process_view
    CustomMiddleware2  process_view
    index.....
    CustomMiddleware2 process_exception
    CustomMiddleware2 process_response
    CustomMiddleware1 process_response
    

      CustomMiddleware2的process_exception在捕获到错误后,把return值作为响应体直接返回了,就不再执行后面的exception了(CustomMiddleware1的),再依次传给process_response即返回给浏览器了。

    三、中间件的应用

    1、做IP访问频率限制

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

    2、URL访问过滤

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

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

    (1)在settings.py中定义白名单:

    # 白名单
    WHITE_LIST = ["/login/", "/reg/", "/logout/"]
    

    (2)创建自定义的中间件文件./app01/my_middlewares.py

    from django.utils.deprecation import MiddlewareMixin
    from django.shortcuts import HttpResponse, redirect
    from authDemo import settings
    
    class AutoMiddleware(MiddlewareMixin):
    
        def process_request(self, request):
            # 拿到白名单
            white_list = settings.WHITE_LIST
            if request.path in white_list:
                # 路径在白名单中直接通过
                return None    # return None等同于不写return ,中间件通过
    
            # 不在白名单中的路径需要校验是否登录验证
            if not request.user.is_authenticated:
                # 未经过校验跳转到登录页面
                return redirect("/login/")
    

    (3)在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.my_middlewares.AutoMiddleware",
    ]
    

      运行程序,并访问index页面,发现跳转到登录页面。

      虽然中间件很方便,但不是所有情况下都应该用中间件,稍有不慎就会降低程序效率。往往视图函数大多数都需要校验,则使用中间件比较合适,只有少量需要校验则还是使用@login_required装饰器更加合适。

    四、中间件源码试读

      主要尝试着读以下两个自带的中间件:

    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    

      

  • 相关阅读:
    数据处理:并行和性能 狼人:
    GTK+3.0终发布:诸多新特性亮相 狼人:
    微软:Windows 7 SP1将于本月正式发布 狼人:
    程序员的进化 狼人:
    TDD并不是看上去的那么美 狼人:
    Bing API 将新增 Bing 空间数据服务 狼人:
    微软产品组里的十一类人 狼人:
    Windows Phone 7“芒果”更新带来浏览器重大升级:IE Mobile 9 狼人:
    UI前沿技术:XNA颜色滚动程序 狼人:
    传递拷贝php 数组 传递 引用
  • 原文地址:https://www.cnblogs.com/xiugeng/p/9314657.html
Copyright © 2011-2022 走看看