第12章:中间件和信号
12.1 中间件系统
中间件概念
中间件是Django处理请求/响应过程中的钩子框架。它是一个轻量级、低级的"插件"系统,用于全局改变Django的输入或输出。
中间件的工作流程:
- 请求到达Django应用时,会依次通过每个中间件的
process_request方法 - 然后通过
process_view方法 - 视图函数处理完成后,响应会依次通过每个中间件的
process_response方法
内置中间件
Django提供了许多内置中间件:
python
# 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',
]常用内置中间件说明:
SecurityMiddleware: 提供安全相关的HTTP头SessionMiddleware: 启用会话支持CommonMiddleware: 处理一些常用功能CsrfViewMiddleware: 提供CSRF保护AuthenticationMiddleware: 将用户与请求关联MessageMiddleware: 启用基于Cookie和会话的消息支持XFrameOptionsMiddleware: 防止点击劫持
自定义中间件
创建自定义中间件:
python
# middleware.py
import time
from django.utils.deprecation import MiddlewareMixin
from django.http import HttpResponse
class TimingMiddleware(MiddlewareMixin):
"""记录请求处理时间的中间件"""
def process_request(self, request):
"""在Django确定要使用的视图之前调用"""
request.start_time = time.time()
print(f"请求开始: {request.path}")
return None # 继续处理请求
def process_view(self, request, view_func, view_args, view_kwargs):
"""在Django调用视图之前调用"""
print(f"即将调用视图: {view_func.__name__}")
return None # 继续处理视图
def process_response(self, request, response):
"""在所有响应返回浏览器之前调用"""
if hasattr(request, 'start_time'):
duration = time.time() - request.start_time
print(f"请求完成: {request.path}, 耗时: {duration:.2f}秒")
return response
class MaintenanceMiddleware(MiddlewareMixin):
"""维护模式中间件"""
def process_request(self, request):
# 检查是否处于维护模式
from django.conf import settings
if getattr(settings, 'MAINTENANCE_MODE', False):
# 检查是否是管理员
if not request.user.is_staff:
return HttpResponse(
'<h1>网站维护中</h1><p>请稍后再访问</p>',
status=503
)
return None
class LoggingMiddleware(MiddlewareMixin):
"""请求日志中间件"""
def process_request(self, request):
# 记录请求信息
import logging
logger = logging.getLogger('request_logger')
logger.info(
f"IP: {self.get_client_ip(request)}, "
f"Method: {request.method}, "
f"Path: {request.path}, "
f"User: {getattr(request.user, 'username', 'Anonymous')}"
)
return None
def get_client_ip(self, request):
"""获取客户端IP地址"""
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
else:
ip = request.META.get('REMOTE_ADDR')
return ip
# 更复杂的中间件示例
class ContentSecurityPolicyMiddleware(MiddlewareMixin):
"""内容安全策略中间件"""
def process_response(self, request, response):
# 添加CSP头
csp_policy = (
"default-src 'self'; "
"script-src 'self' 'unsafe-inline' https://cdn.example.com; "
"style-src 'self' 'unsafe-inline' https://cdn.example.com; "
"img-src 'self' data: https:; "
"font-src 'self' https://fonts.googleapis.com https://fonts.gstatic.com;"
)
response['Content-Security-Policy'] = csp_policy
return response在settings.py中注册中间件:
python
# 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',
# 自定义中间件
'myapp.middleware.TimingMiddleware',
'myapp.middleware.LoggingMiddleware',
'myapp.middleware.MaintenanceMiddleware',
]请求/响应处理
中间件可以处理不同阶段的请求和响应:
python
# advanced_middleware.py
from django.utils.deprecation import MiddlewareMixin
from django.http import JsonResponse
from django.core.exceptions import PermissionDenied
class AdvancedMiddleware(MiddlewareMixin):
"""高级中间件示例"""
def process_request(self, request):
"""
在每个请求上调用,在Django确定要使用的视图之前
返回None继续处理,返回HttpResponse对象则短路处理
"""
# 检查请求频率限制
if self.is_rate_limited(request):
return JsonResponse({'error': '请求过于频繁'}, status=429)
# 添加自定义属性到请求对象
request.custom_data = {
'processed_time': time.time(),
'middleware_version': '1.0'
}
return None
def process_view(self, request, view_func, view_args, view_kwargs):
"""
在Django调用视图之前调用
可以用来修改传递给视图的参数
"""
# 记录视图调用
print(f"调用视图函数: {view_func.__name__}")
# 检查视图是否需要特殊权限
if getattr(view_func, 'require_api_key', False):
api_key = request.META.get('HTTP_X_API_KEY')
if not api_key or not self.validate_api_key(api_key):
raise PermissionDenied("无效的API密钥")
return None
def process_exception(self, request, exception):
"""
当视图抛出异常时调用
可以用来处理特定类型的异常
"""
if isinstance(exception, PermissionDenied):
return JsonResponse({'error': '权限不足'}, status=403)
# 记录异常
import logging
logger = logging.getLogger('exception_logger')
logger.error(f"视图异常: {exception}", exc_info=True)
return None
def process_response(self, request, response):
"""
在所有响应返回浏览器之前调用
可以用来修改响应
"""
# 添加自定义响应头
response['X-Processed-By'] = 'Django-Advanced-Middleware'
# 记录响应时间
if hasattr(request, 'custom_data'):
process_time = time.time() - request.custom_data['processed_time']
response['X-Process-Time'] = f"{process_time:.3f}s"
return response
def is_rate_limited(self, request):
"""检查请求频率限制"""
# 简单实现,实际应用中可以使用Redis等
client_ip = self.get_client_ip(request)
# 这里应该实现实际的频率限制逻辑
return False
def validate_api_key(self, api_key):
"""验证API密钥"""
# 实际应用中应该查询数据库或配置文件
valid_keys = ['valid-key-1', 'valid-key-2']
return api_key in valid_keys
def get_client_ip(self, request):
"""获取客户端IP"""
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
return x_forwarded_for.split(',')[0]
return request.META.get('REMOTE_ADDR')通过装饰器为视图添加中间件功能:
python
# decorators.py
from functools import wraps
def require_api_key(view_func):
"""为视图添加API密钥要求"""
@wraps(view_func)
def wrapper(*args, **kwargs):
return view_func(*args, **kwargs)
wrapper.require_api_key = True
return wrapper
# views.py
from .decorators import require_api_key
@require_api_key
def api_view(request):
return JsonResponse({'message': 'API访问成功'})中间件是Django框架中非常强大的功能,可以用来实现全局的请求处理、安全控制、性能监控等功能。
小结
Django中间件系统提供了强大的请求/响应处理能力:
- ✅ 理解中间件在请求/响应处理链中的位置和作用
- ✅ 掌握内置中间件的功能和配置方法
- ✅ 能够创建自定义中间件处理特定业务需求
- ✅ 了解中间件各处理阶段的执行顺序
- ✅ 通过装饰器为视图添加中间件功能
合理使用中间件能够构建更加灵活和安全的Web应用。
下一篇
我们将学习Django信号系统。