Files
responsex/readme.md
T
2026-09-19 01:31:34 +08:00

222 lines
6.8 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.
# 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()`。