python-django中间件介绍、自定义中间件的方法

471 阅读3分钟

设计

Django框架中的中间件是介于请求与响应处理过程之间的一道处理环节, 它是轻量级且低级的“插件”系统,可以全局改变Django的输入或输出。

这种设计使得我们可以根据需要添加自定义的处理逻辑,以满足各种复杂的需求。

什么是中间件 Middleware ?

注入在 Django 请求/响应 处理流程中的钩子框架,能对 request/response 作处理

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'

每个中间件的功能如下:

  • SecurityMiddleware:为request/response提供了几种安全改进,无它不安全
  • SessionMiddleware:开启session会话支持,无它无session
  • CommonMiddleware:基于APPEND_SLASH和PREPEND_WWW的设置来重写URL,如果APPEND_SLASH设为True,并且初始URL 没有以斜线结尾以及在URLconf 中没找到对应定义,这时形成一个斜线结尾的新URL;如果PREPEND_WWW设为True,前面缺少 www.的url将会被重定向到相同但是以一个www.开头的url。
  • CsrfViewMiddleware:添加跨站点请求伪造的保护,通过向POST表单添加一个隐藏的表单字段,并检查请求中是否有正确的值,无它无csrf保护
  • AuthenticationMiddleware:在视图函数执行前向每个接收到的user对象添加HttpRequest属性,表示当前登录的用户,无它用不了request.user
  • MessageMiddleware:开启基于Cookie和会话的消息支持,无它无message 传递给用户的页面上
  • XFrameOptionsMiddleware:对点击劫持的保护

使用场景

广泛的使用场景

  • 登录认证,安全拦截
  • 日志记录,性能上报
  • 缓存处理,监控告警

例如,我们可以通过编写中间件来实现一些特定的功能,如用户认证服务、CSRF防御机制和点击劫持防御等。

此外,每个中间件都有其特定的职责,它们会按照预定的规则在特定的时候执行。

例如,Django提供的Authentication middleware组件就会在用户请求时将用户与对应的session进行关联。

中间件可以帮助我们实现针对不同路由做不同的异常响应内容。

  • 比如,我们可以针对/pages做静态页面500处理,对/api做rest 500处理,对/graphql做graphql的异常处理。

然而,由于中间件可以修改全局的输入和输出,因此在使用中间件时需要谨慎,用不好可能会影响应用的性能。

总的来说,中间件的设计为Django框架带来了极大的灵活性和扩展性。

自定义中间件的

2 种方法

  • 使用函数实现
  • 使用类实现

函数装饰器

image.png

类装饰器

image.png

创建一个请求日志,性能日志记录中间件

思路:

  • step1 定义实现中间件: def performance_logger_middleware(get_response)
  • step2 记录请求 URL, 参数, 响应时间
  • step3 注册 middleware 到 settings 中
  • step4 配置 日志文件路径

wangdalei_dj/interview/performance.py

import time
import logging

logger = logging.getLogger(__name__)


def performance_logger_middleware(func):
    def middleware(request):
        start_time = time.time()
        
        response = func(request)
        
        duration = time.time() - start_time
        # 将耗时 返回给页面
        response["X-Page-Duration-ms"] = int(duration * 1000)
        logger.info("%s %s %s", duration, request.path, request.GET.dict() )
        return response

    return middleware

将自定义的中间件 注册到配置中 wangdalei_dj/settings/base_settings.py

MIDDLEWARE = [
    'interview.performance.performance_logger_middleware'
    ...
]

其实这个地方在flask中就是 思路是一样的

@deractor_first3
@deractor_first2
@deractor_first1

vim wangdalei_dj/settings/base_settings.py 在日志处理中 单独定义一个handler方式 用于记录性能

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
LOG_DIR = "/data/logs/recruitment/"

LOGGING = {
    'version': 1,
    # 是否禁用已经存在的loggers
    'disable_existing_loggers': False,

    # 日志的格式
    'formatters': {
        'simple': { # exact format is not important, this is the minimum information
            'format': '%(asctime)s %(name)-12s %(lineno)d %(levelname)-8s %(message)s',
        },
    },
    # 日志的处理器
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'simple',
        },
        'mail_admins': {  # Add Handler for mail_admins for `warning` and above
            'level': 'ERROR',
            'class': 'django.utils.log.AdminEmailHandler',
        },
        'file': {
            # 'level': 'INFO',
            'class': 'logging.FileHandler',
            'formatter': 'simple',
            'filename': os.path.join(LOG_DIR, 'recruitment.admin.log'),
        },
        'performance': {
            # 'level': 'INFO',
            'class': 'logging.FileHandler',
            'formatter': 'simple',
            'filename': os.path.join(LOG_DIR, 'xxx.performance.log'),
        },
    },

    'root': {
        'handlers': ['console', 'file'],
        'level': 'INFO',
    },

    'loggers': {
        # 定义中间件的信息
        "django_python3_ldap": {
            "handlers": ["console", "file"],
            "level": "DEBUG",
        },
        "interview.performance": {
            "handlers": ["console", "performance"],
            "level": "INFO",
            "propagate": False,
        },
    },
}

可以看到中间件的日志输出 image.png

cat /data/logs/recruitment/xxx.performance.log

image.png

职位页面也可以看到

image.png

页面的日志中也可以抓到 X-Page-Duration-ms关键字 image.png