responsex
API 响应封装组件,按需加载:核心包不依赖任何 Web/ORM/追踪框架, 用到哪个框架才 import 哪个适配包。
功能
- API 统一响应封装(code / message / data / pagination / trace_id)
- 支持多语言的错误码与错误提示(基于 langx)
- gin 响应体验优化(
responsex/gin) - go-zero trace_id 接入(
responsex/gozerotrace,可选) - 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(推荐)
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() 做了两件事:
- 把 gin 的
c.Set("trace_id", ...)(或X-Request-Id请求头)桥接到标准 context; 没有它,trace_id不会出现在响应里(gin 的c.Value()读不到c.Set()的值)。 - 把
Accept-Language写入语言 context,核心包据此翻译文案。
接入 go-zero 的 trace_id
import _ "code.yun.ink/pkg/responsex/gozerotrace" // 只需这一行
导入后 go-zero 的 OpenTelemetry trace_id 会被自动读取,业务代码无需改动。
(纯 gin 项目如果自己用 c.Set("trace_id", ...) 注入,可以不要这个包。)
GORM 分页
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:
responsex.Success(r.Context(), w, data)
responsex.Error(r.Context(), w, err)
多语言
- 错误码/文案注册(
langx/errorx):
var ErrParamsInvalid = errorx.NewCode("params_invalid", 40001, "参数异常")
// 或按文件注册:code.json 为 map[string]int,其它 json 文件名为语言名
langx.RegisterDir("./langs")
- 输出:
responsex.Error(ctx, w, ErrParamsInvalid.New(ctx)) // 自动带业务码与译文
responsex.FormatMessage(ctx, w, "welcome", map[string]string{"name": "Yun"}, data)
ginx.MsgKV(c, "welcome", "name", "Yun")
- 成功文案:默认
请求成功,可用两种方式覆盖:
// 方式一:全局/单次指定
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 解析顺序
- 已注册的驱动(如 go-zero 适配包);
- 标准 ctx key
responsex.CtxTraceIDKey(值为"trace_id"); - 取不到则响应中不出现
trace_id字段。
框架适配包通过 responsex.RegisterTraceDriver 注册,业务代码一般无需调用。
需要自定义来源时:
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
)
运行测试:
go test ./... # 需要 go.work;否则分别进入各模块执行
cd gin && go test ./...
cd gormx && go test ./...
cd gozerotrace && go test ./...
校验依赖隔离(在核心模块根目录执行,应无任何框架包):
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()。