baseline: 批次A-D 成果 + membership 半成品(测试红)
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
"""请求上下文透传(迭代第 3 轮)。
|
||||
|
||||
**问题**:审计需要 IP / User-Agent,但业务服务层(`sales.services.confirm_sales_bill` 等)
|
||||
拿不到 `request` 对象——它们是被视图调用的纯函数,签名里加 request 会污染业务 API。
|
||||
|
||||
**方案**:中间件在请求进入时把 request 存进 `contextvars.ContextVar`,
|
||||
服务层通过 `current_request()` 读取。用 contextvars 而非 threading.local 是为了
|
||||
在 ASGI(Graniot/asyncio)下也能正确隔离协程——threading.local 在异步场景会串请求。
|
||||
|
||||
安全性:只读用途(审计取 IP/UA),不做鉴权决策;请求结束即清除。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from contextvars import ContextVar
|
||||
from typing import Optional
|
||||
|
||||
_current_request: ContextVar[Optional[object]] = ContextVar(
|
||||
"dealerhub_current_request", default=None
|
||||
)
|
||||
|
||||
|
||||
def set_current_request(request) -> None:
|
||||
"""中间件调用:绑定当前请求。"""
|
||||
_current_request.set(request)
|
||||
|
||||
|
||||
def clear_current_request() -> None:
|
||||
"""中间件调用:解绑(防止跨请求泄漏)。"""
|
||||
_current_request.set(None)
|
||||
|
||||
|
||||
def current_request():
|
||||
"""服务层调用:取当前请求(可能为 None,如后台任务/测试)。"""
|
||||
return _current_request.get()
|
||||
|
||||
|
||||
class RequestContextMiddleware:
|
||||
"""把 request 放进 contextvar,供服务层读取(审计用)。
|
||||
|
||||
注意:本中间件**不**做鉴权或租户解析(那些由 TenantMiddleware / DRF 负责),
|
||||
仅提供"当前请求"的只读视图。
|
||||
"""
|
||||
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
token = _current_request.set(request)
|
||||
try:
|
||||
return self.get_response(request)
|
||||
finally:
|
||||
# 用 reset 精确恢复到进入前的值,避免异步并发下互相覆盖
|
||||
_current_request.reset(token)
|
||||
Reference in New Issue
Block a user