pin_drop当前位置:知识文库 ❯ 图文
Django中间件详解 - 请求响应钩子系统完整指南
一、中间件概述
Django中间件(Middleware)是Django框架中用于处理HTTP请求和响应的钩子系统。它提供了一种轻量级、全局的机制,可以在请求到达视图之前和响应返回客户端之后执行自定义逻辑。中间件按照在MIDDLEWARE配置列表中的顺序依次执行,形成一个"洋葱式"的处理流程。
中间件的常见用途包括:用户认证与权限校验、请求日志记录、跨域资源共享(CORS)处理、接口限流防刷、全局异常处理、请求数据预处理、响应数据后处理等。它是Django扩展请求处理流程的核心机制,几乎所有Django项目都会使用到中间件。
小贴士
Django内置了多个中间件,如SecurityMiddleware(安全相关)、SessionMiddleware(会话管理)、CsrfViewMiddleware(CSRF防护)等,新建项目时会自动配置。
二、中间件语法与结构
从Django 1.10开始,中间件采用新式写法,基于类的__init__和__call__方法实现。以下是最基本的中间件结构:
代码示例
class MyMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# 请求处理(视图之前)
response = self.get_response(request)
# 响应处理(视图之后)
return response
def process_exception(self, request, exception):
# 异常处理
pass
在settings.py中注册中间件:
代码示例
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'myproject.middleware.MyMiddleware', # 自定义中间件
]
三、中间件方法详解
中间件核心方法
返回值说明
-
__call__:必须返回HttpResponse对象,不能返回None
-
process_view:返回None继续执行视图,返回HttpResponse则跳过视图
-
process_exception:返回None继续传播异常,返回HttpResponse停止传播
四、中间件执行流程
中间件的执行遵循"洋葱模型":请求从外到内逐层穿过中间件到达视图,响应从内到外逐层穿过中间件返回客户端。这种设计使得每个中间件都可以在请求和响应两个阶段介入处理。
代码示例
# 请求处理流程
请求 -> Middleware1(前) -> Middleware2(前) -> Middleware3(前) -> View -> 响应处理
|
# 响应处理流程(逆序) |
响应 <- Middleware1(后) <- Middleware2(后) <- Middleware3(后) <----------+
在MIDDLEWARE列表中,位置靠前的中间件先处理请求,但后处理响应。因此中间件的顺序至关重要,需要根据实际业务逻辑合理安排。
小贴士
认证相关的中间件应该放在前面,确保后续中间件可以访问用户信息;而日志中间件通常放在靠后位置,以便记录经过所有处理后的最终响应状态。
五、实战代码示例
示例1:请求计时中间件
这个中间件用于统计每个请求的处理时间,并将耗时信息添加到响应头中,同时将日志记录到日志系统。这是性能监控和优化中最常用的中间件类型。
代码示例
import time
import logging
logger = logging.getLogger(__name__)
class TimingMiddleware:
"""请求计时中间件"""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
start_time = time.time()
response = self.get_response(request)
duration = time.time() - start_time
logger.info(
f'{request.method} {request.path} - '
f'{response.status_code} - {duration:.3f}s'
)
# 在响应头中添加处理时间
response['X-Process-Time'] = f'{duration:.3f}s'
return response
示例2:IP限流中间件
IP限流中间件用于防止恶意请求和接口滥用。它记录每个IP地址的请求时间戳,在指定时间窗口内超过阈值时返回403禁止访问。
代码示例
import time
from django.http import HttpResponseForbidden
class RateLimitMiddleware:
"""IP限流中间件"""
def __init__(self, get_response):
self.get_response = get_response
self.requests = {} # {ip: [timestamp1, timestamp2, ...]}
self.max_requests = 100 # 最大请求数
self.window = 60 # 时间窗口(秒)
def __call__(self, request):
ip = self.get_client_ip(request)
now = time.time()
# 清理过期记录
if ip in self.requests:
self.requests[ip] = [t for t in self.requests[ip] if now - t < self.window]
else:
self.requests[ip] = []
# 检查限流
if len(self.requests[ip]) >= self.max_requests:
return HttpResponseForbidden('请求过于频繁,请稍后再试')
self.requests[ip].append(now)
return self.get_response(request)
def get_client_ip(self, request):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
return x_forwarded_for.split(',')[0].strip()
return request.META.get('REMOTE_ADDR')
示例3:异常处理中间件
异常处理中间件通过process_exception方法捕获视图层未处理的异常。对于API请求,返回格式化的JSON错误响应;对于普通页面请求,则交由Django默认错误处理机制。
代码示例
import logging
from django.conf import settings
from django.http import JsonResponse
logger = logging.getLogger(__name__)
class ExceptionHandlerMiddleware:
"""异常处理中间件"""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
return self.get_response(request)
def process_exception(self, request, exception):
"""处理未捕获的异常"""
logger.error(
f'未处理异常: {exception.__class__.__name__}: {exception}',
exc_info=True
)
# API请求返回JSON错误
if request.path.startswith('/api/'):
return JsonResponse({
'error': '服务器内部错误',
'detail': str(exception) if settings.DEBUG else None,
}, status=500)
# 普通请求返回None,由Django默认处理
return None
六、实际应用场景
-
请求日志记录:记录每个请求的方法、路径、状态码和响应时间,便于后续性能分析和故障排查
-
IP限流防护:防止恶意爬虫、暴力破解和DDoS攻击,保护API接口安全
-
统一异常处理:集中处理应用层异常,为不同请求类型返回友好的错误响应格式
七、注意事项
注意:中间件在MIDDLEWARE列表中的顺序很重要,请求按顺序执行,响应按逆序执行。认证中间件应放在前面,日志中间件放在后面。
注意:中间件的__init__方法只在服务器启动时调用一次,不要在其中存储请求相关数据,否则会导致多线程请求间的状态污染。
注意:process_view返回HttpResponse会跳过视图函数,但列表中后续中间件的process_view仍会继续执行。
注意:新式中间件(基于__call__)和旧式中间件(基于process_request/process_response)不要混用,Django 3.0+已移除旧式中间件支持。
八、练习题
练习1
编写一个中间件,在每个响应头中添加X-Server-Version头,值为Django版本号。
练习2
编写一个中间件,记录每个请求的用户ID、请求路径和响应状态码到日志文件。如果用户未登录,用户ID记录为"anonymous"。
常见问题
Django中间件的执行顺序是怎样的?
中间件按照MIDDLEWARE列表从上到下的顺序处理请求,到达视图后,再按从下到上的逆序处理响应。因此列表最上方的中间件最先处理请求、最后处理响应。
中间件中可以跳过视图直接返回响应吗?
可以。在__call__方法中不调用self.get_response(request),直接返回HttpResponse对象即可跳过视图。这在限流、IP黑名单、权限校验等场景非常实用。
process_exception方法什么时候被调用?
当视图函数抛出未捕获的异常时,Django会从当前中间件开始逆序调用每个中间件的process_exception方法。如果某个中间件返回了HttpResponse,异常传播停止;返回None则继续传播。
如何在中间件中获取当前登录用户信息?
需要在SessionMiddleware和AuthenticationMiddleware之后的中间件中才能访问request.user。因为用户的认证信息是由这些内置中间件设置的。
中间件会影响性能吗?
每个中间件都会在请求-响应周期中增加处理时间。如果中间件逻辑复杂或包含I/O操作,会显著影响性能。建议保持中间件轻量,避免在中间件中进行数据库查询等耗时操作。
本文涉及AI创作
内容由AI创作,请仔细甄别