
本文介绍一种符合 django 最佳实践的请求 id 中间件实现方式,利用 asgiref.local.local(或线程局部存储)为每个请求生成唯一 id,并将其注入日志上下文,避免直接修改 request.meta 导致的兼容性问题和异常。
本文介绍一种符合 django 最佳实践的请求 id 中间件实现方式,利用 asgiref.local.local(或线程局部存储)为每个请求生成唯一 id,并将其注入日志上下文,避免直接修改 request.meta 导致的兼容性问题和异常。
Django 的中间件机制自 1.10 起统一为基于类的 __call__ 形式(即“新式中间件”),而您原始代码中继承 CommonMiddleware 并重写 process_request/process_response 是已弃用的旧式写法(Django <1.10 风格),这正是引发 AttributeError: 'WSGIRequest' object has no attribute '_resource_closers' 的根本原因:旧式中间件钩子与新版请求/响应生命周期不兼容,导致框架内部状态错乱。
✅ 正确做法是实现标准的新式中间件——定义 __init__(self, get_response) 和 __call__(self, request) 方法:
# middleware/request_id_middleware.py
import uuid
from asgiref.local import Local # 推荐:支持 ASGI(如 Django 4.0+ 异步场景)
# 若环境无 asgiref,可回退至 threading.local(仅限同步 WSGI)
# from threading import local as Local
local = Local()
class RequestIDMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# 为当前请求生成唯一 request_id,并绑定到线程/协程局部变量
local.request_id = str(uuid.uuid4())
response = self.get_response(request)
# 在响应头中透出 request_id,便于前端或网关追踪
if hasattr(local, 'request_id'):
response.headers['X-Request-ID'] = local.request_id
return response? 关键改进点说明:
- ✅ 不修改 request.META(易引发不可预知副作用),改用 asgiref.local.Local 安全隔离请求上下文;
- ✅ 兼容同步(WSGI)与异步(ASGI)场景(asgiref.local.Local 是 Django 官方推荐方案);
- ✅ 响应头设置逻辑放在 __call__ 返回前,确保 response 对象已实例化且支持 headers 属性(Django 2.2+)。
? 日志集成:需配合自定义 Filter 将 request_id 注入日志记录:
# logging_filters.py
import logging
from Application import local # 假设 local 定义在 Application/__init__.py
class RequestIDFilter(logging.Filter):
def filter(self, record):
record.request_id = getattr(local, 'request_id', 'N/A')
return True并在 settings.py 中配置日志器:
# settings.py
LOGGING = {
'version': 1,
'filters': {
'request_id': {
'()': 'myapp.logging_filters.RequestIDFilter',
}
},
'handlers': {
'console': {
'level': 'INFO',
'class': 'logging.StreamHandler',
'filters': ['request_id'],
'formatter': 'verbose',
},
},
'formatters': {
'verbose': {
'format': '[{asctime}] {levelname} {request_id} {name} {message}',
'style': '{',
},
},
'loggers': {
'django': {
'handlers': ['console'],
'level': 'INFO',
},
}
}⚠️ 注意事项:
- 确保 RequestIDMiddleware 在 MIDDLEWARE 列表中靠前注册(建议在 SecurityMiddleware 之后、CommonMiddleware 之前),以保证所有后续中间件和视图都能访问该 ID;
- 若使用异步视图(async def),必须使用 asgiref.local.Local,threading.local 在协程中无法正确隔离;
- local.request_id 仅在当前请求生命周期内有效,无需手动清理(asgiref.local.Local 自动管理作用域)。
通过以上实现,您将获得稳定、可追踪、与 Django 生态无缝集成的请求 ID 支持,日志每条记录自动携带 X-Request-ID,大幅提升分布式系统排障效率。


















