签入版本
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
# 请求上下文(ContextVar)
|
||||
|
||||
:::tip 说明
|
||||
`src/core/context.py` 使用 Python 标准库 `contextvars` 管理请求级别的上下文信息,包括当前用户 ID、用户名、姓名和链路追踪 ID。这些变量在整个请求处理链路中自动传递,无需手动传参。
|
||||
:::
|
||||
|
||||
## 核心变量
|
||||
|
||||
```python
|
||||
# src/core/context.py
|
||||
from contextvars import ContextVar
|
||||
|
||||
# 当前登录用户 ID
|
||||
current_user_id: ContextVar[int] = ContextVar("current_user_id", default=0)
|
||||
# 当前登录用户名
|
||||
current_username: ContextVar[str] = ContextVar("current_username", default="")
|
||||
# 当前登录用户姓名
|
||||
current_realname: ContextVar[str] = ContextVar("current_realname", default="")
|
||||
|
||||
# 当前请求的链路追踪 ID
|
||||
current_trace_id: ContextVar[str] = ContextVar("current_trace_id", default="")
|
||||
```
|
||||
|
||||
## 工作原理
|
||||
|
||||
```
|
||||
请求进入
|
||||
│
|
||||
├─ authentication.py: 解析 JWT
|
||||
│ ├─ current_user_id.set(user_id)
|
||||
│ ├─ current_username.set(username)
|
||||
│ └─ current_realname.set(realname)
|
||||
├─ trace_id.py: 生成 TraceID → current_trace_id.set(trace_id)
|
||||
│
|
||||
├─ Handler → Service → Repository
|
||||
│ └─ 任意层可通过 current_realname.get() 获取用户姓名
|
||||
│
|
||||
└─ 请求结束: contextvars 自动清理
|
||||
```
|
||||
|
||||
## 使用场景
|
||||
|
||||
### 1. Repository 层自动填充审计字段
|
||||
|
||||
```python
|
||||
# src/core/base_repository.py
|
||||
class BaseRepository:
|
||||
def _uid(self) -> str:
|
||||
"""获取当前登录用户姓名(从上下文变量读取)"""
|
||||
return current_realname.get()
|
||||
|
||||
def create(self, obj):
|
||||
# 自动填充创建人(姓名)
|
||||
if hasattr(obj, "create_user"):
|
||||
obj.create_user = self._uid()
|
||||
# ...
|
||||
|
||||
def update(self, pk, data):
|
||||
# 自动填充更新人(姓名)
|
||||
data["update_user"] = self._uid()
|
||||
# ...
|
||||
```
|
||||
|
||||
### 2. 操作日志记录
|
||||
|
||||
```python
|
||||
# src/common/middleware/operation_log.py
|
||||
# 操作日志通过异步落库,显式传入 realname(上下文在异步回调中可能已失效)
|
||||
tornado.ioloop.IOLoop.current().spawn_callback(
|
||||
record_operation_log,
|
||||
realname=current_realname.get(),
|
||||
# ...
|
||||
)
|
||||
```
|
||||
|
||||
### 3. 日志输出中携带上下文
|
||||
|
||||
```python
|
||||
# src/core/logger.py
|
||||
# JSON 日志自动携带 trace_id 和 user_id
|
||||
{
|
||||
"timestamp": "2024-01-15T10:30:00",
|
||||
"level": "INFO",
|
||||
"trace_id": current_trace_id.get(),
|
||||
"user_id": current_user_id.get(),
|
||||
"message": "..."
|
||||
}
|
||||
```
|
||||
|
||||
## 设置时机
|
||||
|
||||
### current_user_id / current_username / current_realname
|
||||
|
||||
在 `src/common/middleware/authentication.py` 的 `login_required` 装饰器中设置:
|
||||
|
||||
```python
|
||||
def login_required(func):
|
||||
def wrapper(self, *args, **kwargs):
|
||||
# 解析 JWT Token
|
||||
result = parse_payload(access_token)
|
||||
data = result["data"]
|
||||
|
||||
# 设置上下文(userId / username / realname 均从 JWT payload 读取)
|
||||
current_user_id.set(int(data["userId"]))
|
||||
current_username.set(data.get("username", ""))
|
||||
current_realname.set(data.get("realname", ""))
|
||||
|
||||
return func(self, *args, **kwargs)
|
||||
return wrapper
|
||||
```
|
||||
|
||||
:::tip JWT 载荷
|
||||
登录签发 JWT 时已将 `userId`、`username`、`realname` 写入 payload,认证中间件解析后一并写入上下文。旧 token 中可能不含 `username` / `realname`,`.get()` 会返回空字符串,重新登录后生效。
|
||||
:::
|
||||
|
||||
### current_trace_id
|
||||
|
||||
在 `src/common/middleware/trace_id.py` 的 `apply_trace_id` 中设置:
|
||||
|
||||
```python
|
||||
def apply_trace_id(handler):
|
||||
trace_id = uuid.uuid4().hex[:16]
|
||||
current_trace_id.set(trace_id)
|
||||
handler.set_header("X-Trace-Id", trace_id)
|
||||
```
|
||||
|
||||
## 生命周期
|
||||
|
||||
| 阶段 | 状态 |
|
||||
|:-----|:-----|
|
||||
| 请求进入 | `current_user_id` = 0, `current_username` = "", `current_realname` = "", `current_trace_id` = "" |
|
||||
| `set_default_headers` | `current_trace_id` 被设置 |
|
||||
| `prepare` (login_required) | `current_user_id` / `current_username` / `current_realname` 被设置 |
|
||||
| Handler → Service → Repository | 可随时读取 |
|
||||
| `on_finish` | contextvars 自动清理(下一个请求重置) |
|
||||
|
||||
## 与 Tornado 的区别
|
||||
|
||||
| 方面 | Tornado | Tornado |
|
||||
|:-----|:--------|:--------|
|
||||
| 实现方式 | `contextvars` + 中间件注入 | `contextvars` + 装饰器注入 |
|
||||
| DB 会话 | 中间件注入 `contextvars` | `scoped_session` 线程本地 |
|
||||
| 清理时机 | 中间件 finally | `on_finish` |
|
||||
|
||||
## 总结
|
||||
|
||||
请求上下文通过 `contextvars` 实现请求级别的全局变量传递。`current_user_id` / `current_username` / `current_realname` 在认证装饰器中设置,`current_trace_id` 在请求头处理中设置。Repository 层自动读取 `current_realname` 填充审计字段(`create_user` / `update_user` 写入操作者姓名),日志系统自动读取上下文变量输出链路信息。
|
||||
Reference in New Issue
Block a user