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',

  

 

posted @ 2018-07-15 19:58  休耕  阅读(363)  评论(0编辑  收藏  举报