Files
easygoadmin-wiki/EasyGoAdmin_Gin_NaiveVue/zh/architecture/bind-mechanism.md
T
2026-10-25 17:24:27 +08:00

101 lines
4.0 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.4 反射元数据驱动机制
:::tip 概述
本项目最核心的机制:**RouteSpec 表即路由定义**。`router.Bind` 反射扫描控制器方法,按声明注册路由并写入进程内路由元数据表,权限校验、操作日志、公开白名单、OpenAPI 四个消费方读同一份数据。
:::
## 为什么这样设计
传统做法中路由、权限注解、日志注解分散在三处,漏配一处就出安全洞。本项目把它们收敛为一条声明:
```go
"Page": {Method: "GET", Path: "/api/dept/page", Summary: "部门分页",
Permission: "sys:dept:page", Log: "部门管理-查询分页记录", LogType: "query"},
```
一条声明同时回答:路由怎么注册、谁可以访问、记不记日志、记什么类型。
## Bind 执行流程
```text
registerControllers 内 Bind(rg, new(handler.CXxx), map[string]RouteSpec{...})
│
▼
反射取控制器类型的方法集(含导出方法)
│
▼
按签名过滤:func (c *CXxx) M(ctx context.Context, req *dto.XxxReq) (*dto.XxxRes, error)
│ 签名不匹配 → 跳过(表现为 404,启动期即定)
▼
按方法名查 RouteSpec 表 → 逐条 rg.Handle(Method, Path, 包装函数)
│
▼
包装函数(请求期):
UnmarshalBody 逃生舱判断 → 绑定写入 query→body→path → binding 校验
→ 调 handler 方法 → response 信封写出
│
▼
meta.Register 写入路由元数据表(进程内 map,读写锁保护)
```
## 元数据表(RouteMeta)
```go
// internal/router/meta/meta.go
type RouteMeta struct {
Method string // GET/POST/PUT/DELETE
Path string // 路由模板
Summary string // OpenAPI 摘要
Permission string // 权限节点;空=登录即可
Log string // 日志标题;空=不记录
LogType string // query/add/update/delete/import/export/reset
Public bool // 免登录
ReqType reflect.Type // 请求类型(文档 schema 反射用)
ResType reflect.Type // 响应类型
}
```
存储为进程内内存表(`map["GET /api/dept/page"]RouteMeta` + RWMutex),启动期写入、请求期只读,无持久化与跨进程同步——生命周期与进程一致。
## 四个消费方(同源)
| 消费方 | 读什么 | 行为 |
|---|---|---|
| authz 中间件 | Permission | 校验当前用户权限点集合是否命中;空则只要登录 |
| operation_log 中间件 | Log / LogType | 采集请求摘要异步落库;query 类受 logQuery 开关控制 |
| public 判断 | Public | IsPublicRequest 免登录放行(配合基础设施白名单) |
| OpenAPI 生成 | 全部 + ReqType/ResType | /api.json 与 /swagger 端点按元数据生成文档 |
任一消费方都不再自己维护清单——声明改一处,四处同步生效。
## 绑定细节
- **写入顺序** query → body → path,后写覆盖 → 最终优先级 path > body > query
- **校验** 读 binding 标签,错误消息取 msg 标签,失败直接信封返回
- **逃生舱** 结构体实现 `UnmarshalBody(c *gin.Context) error` 则跳过默认解析(文件上传)
## 基础设施白名单(不经元数据)
`internal/middleware/public.go` 维护少量不经 Bind 注册的免登录路径:
```text
/api/health 健康检查
/api.json, /swagger* 文档端点(api.docs 开关)
/metrics 指标端点(api.metrics 开关)
```
业务路由的公开判断一律走元数据表,两个来源在 `IsPublicRequest` 汇合。
## 对开发的影响
| 场景 | 做法 |
|---|---|
| 新增接口 | RouteSpec 表加一条 + handler 加同名方法,无注解、无散落注册 |
| 改权限码 | 只改声明,同步更新菜单节点 |
| 排查 403 | 看元数据表的 Permission 与角色授权 |
| 生成器 | 生成器自动追加 Bind 声明块——菜单/权限/路由一致性由工具保证 |
## 下一步
→ [4.5 前端分层架构](/zh/architecture/frontend-layer.html)