Define reusable response bodies in .api with typed code mappings, per-route overrides and matching OpenAPI schemas. Add application and callback encoders, early response policy selection, and isolated remote-consumer release verification. Release v0.8.0-preview after full race, vet, build and local MySQL workflow acceptance.
7.9 KiB
公共响应体与独立接口协议
这些功能从 v0.8.0-preview 提供。CLI 与项目框架依赖请固定到同一版本;源码开发可显式使用 --framework-dir。v0.7.0-preview.1 不包含响应模板声明。
go-omni 的响应编码由应用或接口配置。没有配置时保留旧版默认值;业务可以定义自己的字段、嵌套结构、错误码类型、提示和空值行为。response.Response 仅保留为兼容 DTO,运行时与生成器不再通过它固定响应结构。
在 .api 中定义一次、到处引用
response CommonResponse (
body: {
"code": "${code}",
"msg": "${message}",
"data": "${data}"
}
codeType: int32
successCode: 200
errorCode: 9999
successMessage: "操作成功"
dataMode: always
codes: {"USER_NOT_FOUND":404001}
messages: {"USER_NOT_FOUND":"用户不存在"}
)
@server (
group: user
response: CommonResponse
)
service user-api {
@handler getUser
get /users/:userId (GetUserReq) returns (UserResp)
@response raw
@handler rawUser
get /rawUsers/:userId (GetUserReq) returns (UserResp)
}
请求/业务返回类型照常在 type 中定义。上例 UserResp 是业务数据,由模板中的 ${data} 放入公共体。请求失败时,同一模板输出映射后的业务错误。错误本身的 HTTP 状态保持不变,业务 code 与 HTTP 状态相互独立。
- 公共声明可以放进
common.api,其他契约通过import "common.api"复用。 @server (response: CommonResponse)指定整个 service 块的公共体;不同块可以引用同一个模板。@response PartnerResponse为单接口替换模板,成功和错误一起生效。@response raw让成功 JSON 直接返回业务对象,错误沿用组级模板;组本身为 raw 时,错误使用默认模板。- 模板名使用 UpperCamelCase;未知模板、未知配置、错误的占位符和冲突设置在生成前报错。
生成接口以 .api 为响应契约来源。 生成器把选定模板编译进 internal/handler/responseGen.go,在全局鉴权、限流等应用中间件之前选定,然后再执行路由中间件和业务。OpenAPI 与运行时共用配置和映射规则。即使 .api 没有声明模板,生成接口也明确使用默认契约,不会被应用的 YAML 设置静默改变。
业务只需返回业务数据或业务错误,无需每个 Handler 手工组装公共体。生成器维护模板、路由及文档文件,重复生成不覆盖手写 Service、Repository、middleware 和配置。
自定义结构与占位符
公共体没有规定必须出现的字段,也可以把成功和失败分开:
response PartnerResponse (
body: {
"accepted": "${ok}",
"payload": "${data}",
"meta": {"trace":"${requestId}","httpStatus":"${status}"},
"version": 1
}
errorBody: {
"accepted": "${ok}",
"error": {"reason":"${message}","id":"${code}"}
}
)
body 是 JSON 对象,可嵌套对象、数组和常量;errorBody 可选,需要同时定义 body。空对象 {} 也是有效模板。
| 整值占位符 | 输出值 |
|---|---|
${data} |
当前接口的业务返回值,保留对象/数组/数值等原类型 |
${code} |
映射后的 string / int32 / int64 业务错误码 |
${message} |
公开提示,经 message 配置映射 |
${requestId} |
请求标识;为空时省略所在对象字段 |
${ok} |
成功为 true;有错误或 HTTP 状态 ≥400 时为 false |
${status} |
HTTP 状态整数 |
占位符只替换整个值,不执行表达式或字符串拼接。字面量 ${data} 写成 $${data}。数据为 nil 时,dataMode: omitNil(默认)省略所在对象字段;always 输出 null。数组中的占位符保留位置。模板最多嵌套 24 层。
codeType 使用与业务字段一致的类型名,默认 string:
| 类型 | 含义 | OpenAPI |
|---|---|---|
string |
字符串业务码,例如 "OK"、"USER_NOT_FOUND" |
string |
int32 |
32 位有符号整数,−2147483648 到 2147483647 | integer / int32 |
int64 |
64 位有符号整数,−9223372036854775808 到 9223372036854775807 | integer / int64 |
整数类型必须提供 successCode 和 errorCode,支持 0 和负数;默认码和 codes 映射都会校验范围,越界或小数会报错。int64 的配置读取和生成代码保留完整整数精度,并按 JSON 数值输出;消费端也需要支持对应的整数精度。需要文本形式时可用 string 并配置字符串码。
旧写法 integer 保留兼容,沿用 ±(2^53−1) 的数值范围;新配置优先写 int32 或 int64。
codes 按逻辑错误码覆盖默认码;messages 优先于 successMessage / errorMessage。未配置的公开提示沿用原错误消息;内部 cause 不会被默认编码器序列化。
简单的平铺响应也可使用 codeField / messageField / dataField / requestIdField 快捷配置,"-" 禁用字段。空名称沿用旧默认值。这些字段名快捷配置不能和 body 混用,以免两个定义冲突。
手写路由、配置文件与三方回调
手写路由的应用默认配置可以放在 JSON/YAML 的 response 字段,也可用 Go 选项:
app := omni.New(omni.WithResponse(response.Config{
Body: map[string]any{"code":"${code}", "msg":"${message}", "data":"${data}"},
CodeType: "int32", SuccessCode: 200, ErrorCode: 9999, DataMode: "always",
}))
应用之间的配置独立,模板在创建时验证并复制;没有全局可变的响应格式。应用配置快照也会复制模板和映射。响应配置属于静态配置,变更后需重启。
单次显式输出可使用 c.JSON(status, value)、c.Text(status, text)、c.XML(status, value)、c.Bytes(status, contentType, bytes) 或 c.NoContent(),都绕过公共体。已经提交的响应不会再追加错误 JSON。
三方回调可能要求成功与失败都返回 HTTP 200,并分别输出 success / fail。通过接口编码器统一处理成功、错误和 panic:
ack := response.EncoderFunc(func(r response.Result) (response.Payload, error) {
if r.Err != nil || r.Status >= 400 {
return response.Text(200, "fail"), nil
}
return response.Text(200, "success"), nil
})
app.HandleWithResponse("POST", "/hooks/payment", ack, paymentHandler)
编码器也可以设置 HTTP 状态、Header、XML 或任意字节。omni.WithResponseEncoder 设置应用默认编码器;httpx.WithResponse 是普通路由中间件,按中间件顺序生效。需要覆盖全局中间件提前拒绝时,应使用 HandleWithResponse。自定义 Registrar 可以实现 httpx.ResponseRegistrar 提供相同的早期挂载能力;未实现时辅助函数退化为普通路由中间件。
.api 当前声明的是 JSON 协议,@response raw 也是原始 JSON,不代表任意文本/XML协议。自定义文本/XML回调使用上述 Go 入口,并由应用维护相应文档;生成器不会猜测自定义 Go 编码器的输出。如果在命名中间件里替换编码器,业务也必须同步维护文档。
框架内置路由的 404/405 使用应用默认格式;它们不属于某个 .api 分组。TLS 握手、畸形 HTTP、连接级过载等发生在进入应用前,仍按网络层协议处理。自定义编码器失败、panic 或返回非法状态/204 body 时,最终返回空 500,避免递归编码或泄露另一种响应体。
运行示例与验证
go run ./cmd/go-omni api validate --api examples/contracts/responses.api
go run ./cmd/go-omni api generate --api examples/contracts/responses.api \
--module example.com/responseDemo --framework-dir . --dir ../responseDemo
示例业务方法起初为 501,完成 Service 后就会套用声明的模板。参考 完整 .api。验收测试会在临时目录生成真实项目,写入业务方法,执行已生成路由,检查数字错误码、原始 JSON、鉴权错误、路由覆盖与重复生成保护。