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

6.3 KiB
Raw Blame History

4.2 请求处理链路

:::tip 概述 一个受保护的写请求依次穿过七层中间件与 Bind 包装层。理解这条链路是排查 401/403/429/演示拦截/日志缺失的钥匙。注册顺序即执行顺序(router.go s.Use)。 :::

中间件链总览

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 步)

 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    组装记录 → 异步通道 → 批量落库

时序图(受保护写请求 + 日志异步落库)

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

异常分支:

* 任一中间件短路(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 与受信代理

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;看服务端日志