Files
easygoadmin-wiki/EasyGoAdmin_GoFrame_AntdVue/zh/architecture/request-flow.md
T
2026-10-20 21:53:26 +08:00

111 lines
6.3 KiB
Markdown
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.
# 4.2 请求处理链路
:::tip 概述
一个受保护的写请求依次穿过七层中间件与 Bind 包装层。理解这条链路是排查 401/403/429/演示拦截/日志缺失的钥匙。注册顺序即执行顺序(router.go s.Use)。
:::
## 中间件链总览
```text
CORS → Response → RateLimit → Demo → JWT → Tenant → OperationLog → 处理器
(Bind 包装层做权限校验)
```
| # | 中间件 | 短路行为 |
|---|--------|---------|
| 1 | CORS | 预检 204;头设置 |
| 2 | Response | SVG 加固;元信息入 ctx;panic → 500 信封 |
| 3 | RateLimit | 超限 code=429 |
| 4 | Demo | `app.demo=true` 时写操作 code=1015 |
| 5 | JWT | 无效/黑名单 code=401 |
| 6 | Tenant | 租户禁用 1006 / 过期 1005 |
| 7 | OperationLog | 不短路,异步采集 |
| 8 | Bind 包装层 | 权限不足 code=403;参数校验失败 400 |
## 请求生命周期(12 步)
```text
1 CORS 预检/头设置
2 Response 元信息入 ctx;defer panic 兜底
3 RateLimit 固定窗口计数,超限 429
4 Demo 演示模式拦截写操作
5 JWT 解析校验 token → 注入身份;黑名单 401
6 Tenant 租户状态/过期校验(60s 缓存)
7 OperationLog 记 start,Next()
8 Bind 包装层 元数据入 ctx → HasPerm 权限校验(403 短路)→ Parse 参数 → 调 controller
9 controller 转发 service 接口
10 logic ModuleSpec 流水线 / 手写业务;dao.Ctx 查询(钩子已注入条件)
11 response.Write 信封写出(code/body 记入 ctx)
12 OperationLog 组装记录 → 异步通道 → 批量落库
```
## 时序图(受保护写请求 + 日志异步落库)
```text
Client 中间件链 Bind包装/业务 缓存 DB/日志
│ POST /api/user/update │ │ │
│───▶│ CORS 头/预检 │ │ │
│ │ Response: 元信息入ctx, defer recover │ │
│ │ RateLimit ──────────────────────────────────────▶ SET/GET 计数│
│ │ Demo(app.demo=false 放行) │ │ │
│ │ JWT: Parse+黑名单 ───────────────────────────────▶ Exists jti │
│ │ 注入身份; Roles/Perms/DataScope/DeptId ──────▶ 命中缓存 │
│ │ 未命中 → IdentityLoader 回源 ───────────────────────▶ user/role/menu
│ │ Tenant: status/expire ───────────────────────────▶ tenant:info │
│ │ OperationLog: start=now │ │ │
│ │ │ │ │ │
│ │ ▼ ▼ HasPerm(403?短路) │ │
│ │ Parse(req) → logic.Update │ │
│ │ spec 流水线: BeforeUpdate → │ │
│ │ saveTransform → 唯一预检 → ────────────────▶ UPDATE
│ │ (hooks 盖章 update_time/user) │ │
│ │ ▼ │ │ │
│◀───│ WriteJson{code:0,...}(code/body 记 ctx) │ │
│ │ OperationLog: 组装 entry(脱敏 param/result) │ │
│ │ select{ logChan<-entry } 非阻塞 │ │
│ │ worker: 攒批 100条/1s ──────────────────▶ INSERT operation_log
```
异常分支:
```text
* 任一中间件短路(429 / 演示拦截 / 401 / 403)→ 直接 WriteJson,后续不执行
* panic → Response recover → 500 信封(已写出响应则仅记录日志)
* logChan 满 → 丢弃并计数告警(每丢 100 条输出一次 Warning),绝不阻塞业务
```
## 各层关键实现速查
| 层 | 实现位置 | 要点 |
|----|---------|------|
| CORS | middleware/cors.go | `cors.allowOrigin` 空或 `*` 走框架默认;具体来源显式回显凭证 |
| Response | middleware/response.go | .svg 强制 attachment+nosniff(防存储型 XSS);realClientIP/UserAgent 入 ctx |
| RateLimit | middleware/ratelimit.go | 固定窗口;键 `ratelimit:{ip}:{path}:{窗口起点}`;计数经 utility/cache 多实例共享;存储异常放行 |
| Demo | middleware/demo.go | GET/HEAD/OPTIONS 与公开白名单放行;其余写操作拒绝 |
| JWT | middleware/jwt.go | Parse(HS256/过期/签发者)→ 黑名单 Exists → 注入身份(缓存 300s,回源 LoadIdentity) |
| Tenant | middleware/tenant.go | 缓存 60s;租户不存在按禁用处理;数据库基础错误放行(防存储抖动全站不可用) |
| OperationLog | middleware/operation_log.go | 有界通道 2048 + 单 worker;批量 100 条或 1s flush;参数脱敏 password/salt/token → *** |
| Bind | router/bind.go | 权限校验 + r.Parse(gvalid)+ 信封写出;BodyUnmarshaler 逃生舱 |
## 真实 IP 与受信代理
```text
util.go realClientIP:
直连来源不在 api.proxyIps(逗号分隔,精确 IP 与 CIDR)内 → 直接用 GetRemoteIp()
在列表内 → 才信任 X-Forwarded-For 首段 / X-Real-IP
```
防止伪造 XFF 绕过限流或污染日志。Nginx 反代部署务必把反代地址加入 `api.proxyIps`。
## 排障速查
| 症状 | 检查点 |
|------|--------|
| 401 | token 过期/黑名单(登出、轮换、强退都会拉黑 jti) |
| 403 | gmeta permission ↔ 菜单节点 ↔ 角色勾选 ↔ 身份缓存(300s) |
| 429 | 限流阈值;认证入口独立 authLimit=10 |
| 1015 | app.demo=true 拦截写操作 |
| 1005/1006 | 租户过期/禁用(Tenant 中间件,缓存 60s) |
| 日志缺失 | operationLog.enable / log 标签空 / logQuery 关 / 通道满告警 |
| panic 无堆栈 | server.errorStack=false;看服务端日志 |