# responsex API 响应封装组件,**按需加载**:核心包不依赖任何 Web/ORM/追踪框架, 用到哪个框架才 import 哪个适配包。 ## 功能 1. API 统一响应封装(code / message / data / pagination / trace_id) 2. 支持多语言的错误码与错误提示(基于 [langx](https://github.com/yuninks/langx)) 3. gin 响应体验优化(`responsex/gin`) 4. go-zero trace_id 接入(`responsex/gozerotrace`,可选) 5. GORM 分页辅助(`responsex/gormx`,可选) ## 模块与按需加载 本仓库是**多模块**结构,每个模块独立 `go.mod`,依赖互不牵扯: | 模块 | import 路径 | 依赖 | 适用场景 | |---|---|---|---| | 核心 | `code.yun.ink/pkg/responsex` | langx、errorx | 任意 HTTP 框架 | | gin 适配 | `code.yun.ink/pkg/responsex/gin` | gin、核心 | gin 项目 | | go-zero trace | `code.yun.ink/pkg/responsex/gozerotrace` | go-zero、核心 | 需要 go-zero trace_id | | GORM 分页 | `code.yun.ink/pkg/responsex/gormx` | gorm | 需要分页偏移 | 实测依赖隔离效果(`go list -deps`,外部包数 / 是否引入 go-zero): | 消费场景 | 模块图 | 外部包 | go-zero | gin | gorm | |---|---|---|---|---|---| | 只用核心包 | 4 | 4 | ✗ | ✗ | ✗ | | 核心 + gin | 69 | 57 | ✗ | ✓ | ✗ | | 核心 + gin + gorm | 82 | 69 | ✗ | ✓ | ✓ | | 核心 + gin + gozerotrace | 174 | 210 | ✓ | ✓ | ✗ | 关键点:**不 import `gozerotrace` 就不会下载、不会链接 go-zero** (含 OpenTelemetry / gRPC / zipkin / jaeger 等一整棵依赖树)。 ## 快速开始 ### 纯 gin(推荐) ```go import ( "code.yun.ink/pkg/responsex" ginx "code.yun.ink/pkg/responsex/gin" "github.com/gin-gonic/gin" ) func main() { r := gin.New() r.Use(ginx.Middleware()) // 桥接 trace_id 与 Accept-Language r.GET("/ping", func(c *gin.Context) { c.Set("trace_id", "xxx") // 有就自动出现在响应里 ginx.Success(c, gin.H{"pong": true}) }) r.GET("/fail", func(c *gin.Context) { ginx.ErrorStr(c, "参数异常,请检查参数") }) r.Run() } ``` `ginx.Middleware()` 做了两件事: 1. 把 gin 的 `c.Set("trace_id", ...)`(或 `X-Request-Id` 请求头)桥接到标准 context; 没有它,`trace_id` 不会出现在响应里(gin 的 `c.Value()` 读不到 `c.Set()` 的值)。 2. 把 `Accept-Language` 写入语言 context,核心包据此翻译文案。 ### 接入 go-zero 的 trace_id ```go import _ "code.yun.ink/pkg/responsex/gozerotrace" // 只需这一行 ``` 导入后 go-zero 的 OpenTelemetry trace_id 会被自动读取,业务代码无需改动。 (纯 gin 项目如果自己用 `c.Set("trace_id", ...)` 注入,可以不要这个包。) ### GORM 分页 ```go import "code.yun.ink/pkg/responsex/gormx" var list []User gormx.GetDbOffset(db, page, size).Find(&list) respgin.SuccessWithPage(c, list, responsex.GetPage(page, size, total)) ``` ### 不使用 gin 的其它框架 核心包只依赖标准库的 `context` 与 `http.ResponseWriter`: ```go responsex.Success(r.Context(), w, data) responsex.Error(r.Context(), w, err) ``` ## 多语言 1. 错误码/文案注册(`langx` / `errorx`): ```go var ErrParamsInvalid = errorx.NewCode("params_invalid", 40001, "参数异常") // 或按文件注册:code.json 为 map[string]int,其它 json 文件名为语言名 langx.RegisterDir("./langs") ``` 2. 输出: ```go responsex.Error(ctx, w, ErrParamsInvalid.New(ctx)) // 自动带业务码与译文 responsex.FormatMessage(ctx, w, "welcome", map[string]string{"name": "Yun"}, data) ginx.MsgKV(c, "welcome", "name", "Yun") ``` 3. 成功文案:默认 `请求成功`,可用两种方式覆盖: ```go // 方式一:全局/单次指定 responsex.InitOptions(responsex.SetDefaultSuccessMessage("操作成功")) // 方式二:注册专用 key "responsex_success",按请求语言输出 langx.AppendTrans("zh", map[string]string{"responsex_success": "请求成功"}) langx.AppendTrans("en", map[string]string{"responsex_success": "success"}) ``` > 注意:`responsex_success` 是专用 key。不要用 `success`,该 key 已被 errorx 预注册。 ## trace_id 解析顺序 1. 已注册的驱动(如 go-zero 适配包); 2. 标准 ctx key `responsex.CtxTraceIDKey`(值为 `"trace_id"`); 3. 取不到则响应中不出现 `trace_id` 字段。 框架适配包通过 `responsex.RegisterTraceDriver` 注册,业务代码一般无需调用。 需要自定义来源时: ```go responsex.RegisterTraceDriver(func(ctx context.Context) string { return myTracer.IDFrom(ctx) }) ``` ## 主要 API 核心(`responsex`): - 响应:`Success`、`SuccessWithPage`、`Error`、`ErrorWithData`、`ErrorStr`、`FormatMessage`、`ResponseCtx` - 选项:`SetCode`、`SetMessage`、`SetData`、`SetPage`、`SetHttpStatusCode`、`SetHeader(s)`、`SetIgnoreLog`、`SetLogger`、`SetIgnoreTrace`、`SetDefaultSuccessCode`、`SetDefaultErrorCode`、`SetDefaultSuccessMessage` - 分页:`GetOffset`、`GetPage`、`Pagination` - trace:`SetTraceID`、`TraceID`、`RegisterTraceDriver`、`CtxTraceIDKey`、`SuccessMessageCtx` gin(`ginx`):`Middleware`、`Success`、`SuccessWithPage`、`Error`、`ErrorWithData`、`ErrorStr`、`Custom`、`MsgMapData`、`MsgKV` gormx:`GetDbOffset` gozerotrace:空导入即注册 ## 开发 本仓库是多模块,本地开发建议在根目录建 `go.work`(已 gitignore,勿提交): ``` go 1.22.4 use ( . ./gin ./gormx ./gozerotrace ) ``` 运行测试: ```bash go test ./... # 需要 go.work;否则分别进入各模块执行 cd gin && go test ./... cd gormx && go test ./... cd gozerotrace && go test ./... ``` 校验依赖隔离(在核心模块根目录执行,应无任何框架包): ```bash go list -deps code.yun.ink/pkg/responsex | grep -E 'gin-gonic|gorm.io|zeromicro|opentelemetry' ``` ## 发布(tag 规则) 子模块必须带目录前缀的 tag,否则 proxy 不认: | 模块 | tag | 说明 | |---|---|---| | 核心 | `v1.0.2` | 根目录 | | gin | `gin/v1.0.2` | 子模块 | | gormx | `gormx/v1.0.2` | 子模块 | | gozerotrace | `gozerotrace/v1.0.2` | 子模块 | **发布顺序**(重要):Go 无法 require 本地未发布的模块,所以必须 **先推子模块 tag,最后再改根 go.mod 并发布根版本**。 ## v0.0.4 → v1.x 迁移 gin 适配层从根包移到了子模块,并改名为 `ginx`: | 旧(v0.0.4,根包) | 新(v1.x) | |---|---| | `responsex.GinError` | `ginx.Error` | | `responsex.GinErrorData` | `ginx.ErrorWithData` | | `responsex.GinErrorStr` | `ginx.ErrorStr` | | `responsex.GinSuccess` | `ginx.Success` | | `responsex.GinSuccessWithPage` | `ginx.SuccessWithPage` | | `responsex.GinOptions` | `ginx.Custom` | | `responsex.GinMsgMapData` | `ginx.MsgMapData` | | `responsex.GinMsgKV` | `ginx.MsgKV` | 新增的 `ops ...Option` 是变参,旧调用点语义不变;只需把 import 与函数名改掉, 并在路由上挂一次 `ginx.Middleware()`。