Django中间件详解

一.中间件概念  

   中间件顾名思义,是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出。因为改变的是全局,所以需要谨慎实用,用不好会影响到性能。我们从浏览器发出一个请求 Request,得到一个响应后的内容 HttpResponse ,这个请求传递到 Django的过程如下:

            

  

   也就是说,每一个请求都是先通过中间件中的 process_request 函数,这个函数返回 None 或者 HttpResponse 对象,如果返回前者,继续处理其它中间件,如果返回一个 HttpResponse,就处理中止,返回到网页上。中间件不用继承自任何类(可以继承 object ),下面一个中间件大概的样子:

class CommonMiddleware(object):
    def process_request(self, request):
        return None

    def process_response(self, request, response):
        return response

    除了上面两个经常使用的函数外,还有 process_view, process_exception 和 process_template_response 函数。因此中间件中经常使用的函数就是如上的五种。而且中间件随着Django的版本不同也会不同,这点我们将在下面具体讨论。

二.实际应用需求:

    比如我们要做一个 拦截器,发现有恶意访问网站的人,就拦截他!假如我们通过一种技术,比如统计一分钟访问页面的次数,太多就把他的 IP 加入到黑名单 BLOCKED_IPS(这部分没有提供代码,主要讲中间件部分):

这里的代码的功能就是 获取当前访问者的 IP (request.META[‘REMOTE_ADDR‘]),如果这个 IP 在黑名单中就拦截,如果不在就返回 None (函数中没有返回值其实就是默认为 None),把这个中间件的 Python 路径写到settings.py中即可生效,这里只是举一个简单的例子,后面会详细举例:
class BlockedIpMiddleware(object):
    def process_request(self, request):
        # 这里使用反射的方式拿到settings.py文件中的BLOCKED_IPS,其是一个列表,如果获取不到
        # 那就返回一个空列表
        """
        因此最终实现的效果就是:BLOCKED_IPS = [‘192.168.4.128‘, ‘192.168.4.129‘, ‘192.168.4.130‘]
        if ‘192.168.4.129‘ in [‘192.168.4.128‘, ‘192.168.4.129‘, ‘192.168.4.130‘]:
            return http.HttpResponseForbidden(‘<h1>Forbidden</h1>‘)
        如果获取不到BLOCKED_IPS,那就返回一个空列表:
        if ‘192.168.4.129‘ in []:
           这就不满足条件,所以不执行
        """
        if request.META[‘REMOTE_ADDR‘] in getattr(settings, "BLOCKED_IPS", []):
            return http.HttpResponseForbidden(‘<h1>Forbidden</h1>‘)

1.1 Django 1.9 和以前的版本的中间件如下:

MIDDLEWARE_CLASSES = (  # 重点关注这里的名称
    ‘django.contrib.sessions.middleware.SessionMiddleware‘,
    ‘django.middleware.common.CommonMiddleware‘,
    ‘django.middleware.csrf.CsrfViewMiddleware‘,
    ‘django.contrib.auth.middleware.AuthenticationMiddleware‘,
    ‘django.contrib.auth.middleware.SessionAuthenticationMiddleware‘,
    ‘django.contrib.messages.middleware.MessageMiddleware‘,
    ‘django.middleware.clickjacking.XFrameOptionsMiddleware‘,
    ‘django.middleware.security.SecurityMiddleware‘,
    ‘MiddlewareProject.Middleware.my_middleware_dd.BlockedIpMiddleware‘ # 自定义的中间件
)

1.2 Django 1.10 版本 更名为 MIDDLEWARE(单复同形),写法也有变化,部署的时候要重新修改名字

如果用 Django 1.10版本开发,部署时用 Django 1.9版本或更低版本,要特别小心此处。

MIDDLEWARE = (
    ‘django.contrib.sessions.middleware.SessionMiddleware‘,
    ‘django.middleware.common.CommonMiddleware‘,
    ‘django.middleware.csrf.CsrfViewMiddleware‘,
    ‘django.contrib.auth.middleware.AuthenticationMiddleware‘,
    ‘django.contrib.auth.middleware.SessionAuthenticationMiddleware‘,
    ‘django.contrib.messages.middleware.MessageMiddleware‘,
    ‘django.middleware.clickjacking.XFrameOptionsMiddleware‘,
    ‘django.middleware.security.SecurityMiddleware‘,
    ‘MiddlewareProject.Middleware.my_middleware_dd.BlockedIpMiddleware‘
)

Django 会从 MIDDLEWARE_CLASSES 或 MIDDLEWARE 中按照从上到下的顺序一个个执行中间件中的 process_request 函数,而其中 process_response 函数则是最前面的最后执行,关于具体的执行顺序笔者将在后面做一个详细的总结。

二,再比如,我们在网站放到服务器上正式运行后,DEBUG改为了 False,这样更安全,但是有时候发生错误我们不能看到错误详情,调试不方便,有没有办法处理好这两个事情呢?普通访问者看到的是友好的报错信息;管理员看到的是错误详情,以便于修复 BUG;当然可以有,利用中间件就可以做到!代码如下:

import sys
from django.views.debug import technical_500_response
from django.conf import settings

class UserBasedExceptionMiddleware(object):
    def process_exception(self, request, exception):
        if request.user.is_superuser or request.META.get(‘REMOTE_ADDR‘) in settings.INTERNAL_IPS:
            return technical_500_response(request, *sys.exc_info())

把这个中间件像上面一样,加到你的 settings.py 中的 MIDDLEWARE_CLASSES 中,可以放到最后,这样可以看到其它中间件的 process_request的错误。

当访问者为管理员时,就给出错误详情,比如访问本站的不存在的页面:http://www.ziqiangxuetang.com/admin/

作为一个普通的访问者,我们看到的是一个比较友好的普通的提示信息:

三.补充:Django 1.10 接口发生变化,变得更加简洁

class SimpleMiddleware(object):
    def __init__(self, get_response):
        self.get_response = get_response
        # One-time configuration and initialization.

    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.
        # 调用 view 之前的代码

        response = self.get_response(request)

        # Code to be executed for each request/response after
        # the view is called.
        # 调用 view 之后的代码

        return response

Django 1.10.x 也可以用函数来实现中间件,详见官方文档:https://docs.djangoproject.com/en/1.10/topics/http/middleware/#writing-your-own-middleware

四.写出一个兼容Django各版本的中间件

try:
    from django.utils.deprecation import MiddlewareMixin  # Django 1.10.x
except ImportError:
    MiddlewareMixin = object  # Django 1.4.x - Django 1.9.x

class SimpleMiddleware(MiddlewareMixin):
    def process_request(self, request):
        pass

    def process_response(request, response):
        pass

新版本中 django.utils.deprecation.MiddlewareMixin 的 源代码 如下:

class MiddlewareMixin(object):
    def __init__(self, get_response=None):
        self.get_response = get_response
        super(MiddlewareMixin, self).__init__()

    def __call__(self, request):
        response = None
        if hasattr(self, ‘process_request‘):
            response = self.process_request(request)
        if not response:
            response = self.get_response(request)
        if hasattr(self, ‘process_response‘):
            response = self.process_response(request, response)
        return response

__call__ 方法会先调用 self.process_request(request),接着执行 self.get_response(request) 然后调用 self.process_response(request, response).旧版本(Django 1.4.x-Django 1.9.x) 的话,和原来一样。

六.实例验证

   在讲解中间件时,一个Django项目的起始执行如下:先走WSGI,然后再走中间件,注意一点,自带的中间件的顺序是不能倒置的,因为有可能下一个中间件要依赖于上一个中间件的数据,如果随意颠倒顺序,会报错,我们通常将自定义的中间件放到最下面,本次我们使用的是Django 1.8版本:

MIDDLEWARE_CLASSES = (
    ‘django.contrib.sessions.middleware.SessionMiddleware‘,
    ‘django.middleware.common.CommonMiddleware‘,
    ‘django.middleware.csrf.CsrfViewMiddleware‘,
    ‘django.contrib.auth.middleware.AuthenticationMiddleware‘,
    ‘django.contrib.auth.middleware.SessionAuthenticationMiddleware‘,
    ‘django.contrib.messages.middleware.MessageMiddleware‘,
    ‘django.middleware.clickjacking.XFrameOptionsMiddleware‘,
    ‘django.middleware.security.SecurityMiddleware‘,   # 自定义中间件
    ‘MiddlewareDemo.Middleware.my_middleware_dd.MiddleWare1‘,
    ‘MiddlewareDemo.Middleware.my_middleware_dd.MiddleWare2‘
)
"""
注意在上面,如果自定义的中间件my_middleware.py放在项目根目录下,那么在settings中设置的路径直接就是my_middleware.MiddleWare1
Django能识别的只是根目录,我们通常会自定义一个目录,专门用来存放自定义的中间件
"""

接下来,我们一起看看中间件的执行流程,来看一张简单的图:

通过上图,我们可以总结出如下的流程:

request请求经过WSGI后,先进入中间件,依然开始先走process_request函数,然后走路由关系映射后,这里注意
并没有直接进入视图函数,而是从头开始执行process_view()函数;然后再去执行与urls.py匹配的视图函数;
如果视图函数没有报错,那就直接挨个反过来从最后一个中间件开始,依次将返回的实例对象(也就是我们在视图函数中
写的 return HttpResponse()等等)传递给每个中间件的process_response函数;最后再交给客户端浏览器;
如果执行视图函数出错,那就反过来从最后一个中间件开始,将错误信息传递给每个中间件的process_exception()函数,走完所有后,然后最终再走procss_response后,最终再交给客户端浏览器
注意:视图函数的错误是由process_exception()函数处理的,从最后一个中间件开始,依次逐级提交捕捉到的异常
然后最终交给procss_response()函数,将最终的错误信息交给客户端浏览器。

process_view的作用和特殊作用在哪?

走process_request的时候不知道走url中的哪个视图函数,当我们再回来走process_view的时候,由于此时已经走了
urls.py文件,所以它已经知道该执行哪个视图函数了我们发现process_view的源码中它的参数多了个callback这个参数的值就是具体的我们要执行视图函数;因此我们可以总结其实在process_view中可以执行下我们的视图函数。
def process_view(self, request, callback, callback_args, callback_kwargs):这里的callback就是视图函数的名称,因此如果有特殊需求,既不想进入views.py文件中执行视图函数,但是又想在中间件层面执行下视图函数,可以在process_view中,直接调用视图函数的名称即可执行:index()

我们首先来看下面的实际例子:

自定义的中间件如下:

# _*_ coding:utf-8 _*_

try:
    from django.utils.deprecation import MiddlewareMixin  # Django 1.10.x
except ImportError:
    MiddlewareMixin = object

"""
process_request是不需要加return的,我们观察,如果假如return
会发生什么
"""

class MiddleWare1(object):
    def process_request(self, request):
        print("MQ1  request=======>")

    def process_response(self, request, response):
        print("MQ1  response=======>")
        """
        这里一定要返回response,作为中间件的返回
        如果不加浏览器会报错:
        MiddleWare1.process_response didn‘t return an HttpResponse object.
        It returned None instead.
        也就是说process_response一定要返回一个response
        这里的response其实质就是我们在视图函数中返回给浏览器的返回值:
        return HttpResponse("OK")
        也就是说,视图函数将HttpResponse("OK")作为一个参数传递给中间件的
        process_response()函数,然后由中间件一层层地往上面传递给其他中间件
        最终显示给客户端浏览器;所以是先打印view is running,然后再打印
        MQ1 response
        """
        return response

视图函数如下:

from django.shortcuts import render, HttpResponse

# Create your views here.

def index(request):
    print("view index is running")

    return HttpResponse("index..")

最终打印结果:

MQ1  request=======>
view index is running
MQ1  response=======>

终端的打印结果:

结果印证了我们上面的执行流程,先走process_request()函数,然后再走视图函数,最后再走process_response()函数
所以是先打印view is running,然后再打印MQ1 response。

接着wo‘men

原文地址:https://www.cnblogs.com/pyspark/p/8352756.html

时间: 2024-11-01 13:37:54

Django中间件详解的相关文章

net core 中间件详解及项目实战

net core 中间件详解及项目实战 前言 在上篇文章主要介绍了DotNetCore项目状况,本篇文章是我们在开发自己的项目中实际使用的,比较贴合实际应用,算是对中间件的一个深入使用了,不是简单的Hello World,如果你觉得本篇文章对你有用的话,不妨点个[推荐]. 目录 中间件(Middleware)的作用 中间件的运行方式 中间件(Middleware)和过滤器(Filter)的区别 什么情况我们需要中间件 怎么样自定义自己的中间件 中间件(Middleware)的作用 我们知道,任何

22:django 配置详解

django配置文件包含了你的django安装的所有配置信息,本节为大家详细讲解django的配置 基本知识 一个配置文件只是一个包含模块级别变量的的python模块,所有的配置变量都是大写的,哈哈哈,这是一个简单的例子 DEBUG = False DEFAULT_FROM_EMAIL = '[email protected]' TEMPLATE_DIRS = ('/home/templates/mike', '/home/templates/john') 因为python的配置文件也是一个py

Python全栈之路--Django ORM详解

ORM:(在django中,根据代码中的类自动生成数据库的表也叫--code first) ORM:Object Relational Mapping(关系对象映射) 我们写的类表示数据库中的表 我们根据这个类创建的对象是数据库表里的一行数据 obj.id  obj.name.....就是数据库一行数据中的一部分数据 ORM--First: 我们在学习django中的orm的时候,我们可以把一对多,多对多,分为正向和反向查找两种方式. class UserType(models.Model):

Python之路【第二十一篇】Django ORM详解

ORM回顾 关系对象映射(Object Relational Mapping,简称ORM). django中遵循 Code Frist 的原则,即:根据代码中定义的类来自动生成数据库表. 对于ORM框架里: 我们写的类表示数据库的表 如果根据这个类创建的对象是数据库表里的一行数据 对象.id 对象.value 是每一行里的数据 http://www.cnblogs.com/luotianshuai/p/5301343.html 梳理 首先在理解ORM的时候,我们可以把一对多.多对多 分为正向和反

ASP.NET Core 中间件详解及项目实战

前言 在上篇文章主要介绍了DotNetCore项目状况,本篇文章是我们在开发自己的项目中实际使用的,比较贴合实际应用,算是对中间件的一个深入使用了,不是简单的Hello World,如果你觉得本篇文章对你有用的话,不妨点个[推荐]. 目录 中间件(Middleware)的作用 中间件的运行方式 中间件(Middleware)和过滤器(Filter)的区别 什么情况我们需要中间件 怎么样自定义自己的中间件 中间件(Middleware)的作用 我们知道,任何的一个web框架都是把http请求封装成

lumen 中间件详解

我来给大家,讲解一下lumen中的中间件,高手勿喷. 首先,我们看下lumen中文档中的写法,我这里看的是5.3中文文档.https://lumen.laravel-china.org/docs/5.3/middleware lumen中会自带一个示例的中间件,我就在这上面改了,用的也是文档的例子. 1.找到ExampleMiddleware.php文件 2.创建一个路由,并使用ExampleMiddleware $app->group(['middleware' => 'Example'],

Django框架详解之url

Django基本命令 下载Django pip3 install django 创建一个django project django-admin.py startproject cms 当前目录下会生成cms的工程,目录结构如下: setting.py --- 包含了项目的默认设置,包括数据库信息,调试标志以及其他一些工作的变量 urls.py --- 负责把url模式映射到应用程序 wsgi.py --- 用于开发服务器和生产WSGI部署 manage.py --- Django项目里面的工具,

django http404 详解

[引子] 今天在看django的官方文档的时候看到get_object_or_404这个函数感觉比较奇怪.这个主要来自于它的功能,如果要查询的对象 存在那么就返回对象:如果对象不存在那么就要报404 Not Found :但是404 Not Found并不是它的返回值,而django直接返回 了404页面.这个感觉就比较奇怪了. 1.对get_object_or_404的一次失败地模拟 from django.shortcuts import render from django.http im

linux上安装Python和django流程详解

1.安装python3 1.1 安装相关依赖库(工具包)  --->很重要 yum install gcc patch libffi-devel python-devel zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel -y 1.2 进入opt(用于存放软件)目录 1.3 Pytho