Files
go-omni/RESPONSE.md

9.4 KiB
Raw Permalink Blame History

公共响应体与独立接口协议

v0.26 新增的 国际化适配保留现有响应契约。Result.Message 是校验使用的目录原文;MessageOverride 是可选展示译文,PublicMessage() 返回最终文案。内置 Formatter 自动使用译文;自定义 Encoder 显式选择,固定文本/XML 回执和直接输出不自动改写。

这些功能从 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 提供相同的早期挂载能力;未实现时辅助函数退化为普通路由中间件。

@response raw 表示原始 JSON,不代表任意文本/XML协议。从 v0.9.0-preview 开始,.api 可用 @handlerMode manual 配合 @produces 描述文本/文件等成功输出,详见 迁移扩展。自定义回调错误协议仍使用上述 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、鉴权错误、路由覆盖与重复生成保护。

错误目录与 HTTP 200 失败(v0.10.0-preview)

错误含义由项目的 errors.Catalog 或 .api errorCatalog 集中定义,返回字段/结构仍由本页的 response 模板控制。目录提供独立的逻辑标识、对外字符串/整数码、消息、HTTP 状态,使用 WithErrors 或生成路由绑定。详见 统一错误目录。

response.Result.IsError() 综合明确失败标记、Err 和 HTTP 状态判断;HTTP 200 的业务错误不会使用成功码/成功提示,仍可选择 errorBody。新目录的公开码与提示优先于通用兜底;与同标识 Codes/Messages 冲突会拒绝,不会静默覆盖。没有目录的旧映射保持原有行为。

自定义 Encoder 可读取 Code(逻辑标识)、BusinessCode(目录对外码)、Message 与 Err,并返回项目要求的最终状态和媒体类型。不要仅通过 Status 判断成功;内部原因默认只供诊断,不应由 Encoder 直接序列化 Err。RawJSON 的成功输出保持原样,失败继续交给所选错误编码器。