Files
dealerhub/backend/apps/core/context.py
T

55 lines
1.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""请求上下文透传(迭代第 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)