113 lines
3.8 KiB
Markdown
113 lines
3.8 KiB
Markdown
# 4.2 请求处理链路
|
||
|
||
:::tip 链路概览
|
||
一个典型 API 请求经过:浏览器 →(Nginx/Vite 代理)→ 中间件链 → 反射路由绑定 → handler → service → repository → 数据库 → 统一信封返回。本页以 `GET /api/example/page` 为例逐阶段拆解。
|
||
:::
|
||
|
||
## 第一阶段:网络层
|
||
|
||
前端经封装发起请求(`ui/src/api/tool/example.ts`):
|
||
|
||
```typescript
|
||
// ui/src/api/tool/example.ts(摘录)
|
||
import { http } from '@/utils/http/axios';
|
||
|
||
// Authorization: Bearer <access_token> 由请求拦截器自动携带
|
||
export function getExamplePage(params?: Record<string, any>) {
|
||
return http.request({ url: '/example/page', method: 'GET', params });
|
||
}
|
||
```
|
||
|
||
开发期 Vite 代理转发(`ui/.env.development`):
|
||
|
||
```text
|
||
VITE_PROXY=[["/api", "http://127.0.0.1:8000/api"]]
|
||
```
|
||
|
||
生产由 Nginx 反代到 8000(见 deploy/nginx.conf)。
|
||
|
||
## 第二阶段:中间件链(顺序勿动)
|
||
|
||
```text
|
||
trace 分配 trace_id,后续全部日志可关联
|
||
↓
|
||
CORS 跨域响应头(cors.allowOrigin)
|
||
↓
|
||
Metrics Prometheus 指标采集(请求数/耗时)
|
||
↓
|
||
Response 统一信封出口 + panic 恢复(必须包住其后全部 handler)
|
||
↓
|
||
RateLimit 固定窗口限流(认证接口配额更严)
|
||
↓
|
||
Demo 演示模式:写操作拒绝,读与登录放行
|
||
↓
|
||
JWT 解析 Token,注入身份(公开接口跳过)
|
||
↓
|
||
Tenant 加载租户上下文(白名单表隔离依据)
|
||
↓
|
||
Authz 按路由元数据 Permission 校验权限点
|
||
↓
|
||
OperationLog 采集请求摘要,链尾异步落库(须读到最终响应)
|
||
```
|
||
|
||
顺序是设计约束:trace 最先(日志依赖 trace_id);Response 在业务前(panic 恢复包住全链);限流/演示在身份前(防未认证高频攻击);JWT → 租户 → 权限逐层依赖前置注入的身份;日志链尾采集。
|
||
|
||
## 第三阶段:路由绑定
|
||
|
||
`router.Bind` 已在启动期把 `GET /api/example/page` 注册到 Gin 路由树。请求命中后,包装函数执行:
|
||
|
||
```text
|
||
绑定写入:query(pageNo/pageSize/name/type/status/keyword)→ body → path
|
||
│ 同名后写覆盖,最终 path > body > query
|
||
▼
|
||
binding 标签校验(失败直接 4xx 信封返回,不进 handler)
|
||
│
|
||
▼
|
||
构造 dto.ExamplePageReq → 调用 CExample.Page
|
||
```
|
||
|
||
## 第四阶段:四件套调用
|
||
|
||
```go
|
||
// handler:一行委托
|
||
service.Example.Page(ctx, req)
|
||
|
||
// service:委托基座
|
||
list, total, err := exampleSpec.Page(ctx, req, req.GetPageNo(), req.GetPageSize())
|
||
|
||
// ModuleSpec:applyFilters 固定顺序生效
|
||
Model(Ctx 已注入 del_flag=0)→ LikeFields(name) → EqFields(type,status)
|
||
→ KeywordFields → Filter → DataScope → OrderBy → 分页 Find
|
||
```
|
||
|
||
ctx 贯穿全链——身份、租户、事务连接都挂在 ctx 上,repository 经 `db.FromContext(ctx)` 取连接。
|
||
|
||
## 第五阶段:响应与日志
|
||
|
||
```text
|
||
service 返回 PageData[ExampleItem]
|
||
↓
|
||
绑定层包装函数调用 response.Ok(data)
|
||
↓
|
||
Response 中间件信封输出 {code:0, ok:true, msg, data}
|
||
↓
|
||
OperationLog 中间件读取最终响应摘要,异步写 gin_operation_log
|
||
(LogType=query 时还受 operationLog.logQuery 开关控制)
|
||
```
|
||
|
||
失败路径:binding 校验失败 / apperr 错误 / 普通 error / panic,全部经 Response 中间件映射为对应 code 的 JSON 信封,绝不出现 HTML 错误页(NoRoute 也返回 JSON)。
|
||
|
||
## 关键观察点
|
||
|
||
| 阶段 | 观察手段 |
|
||
|---|---|
|
||
| 全链追踪 | 响应头/日志中的 trace_id |
|
||
| SQL | `database.default.debug=true` 打印 SQL |
|
||
| 权限 | Swagger(/swagger)查看接口权限码 |
|
||
| 指标 | /metrics 看请求计数与耗时分布 |
|
||
| 日志落库 | gin_operation_log 表 |
|
||
|
||
## 下一步
|
||
|
||
→ [4.3 后端分层架构](/zh/architecture/backend-layer.html)
|