# 公共响应体与独立接口协议 v0.26 新增的 [国际化适配](i18n/README.md#接入-go-omni-错误与-http)保留现有响应契约。`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 中定义一次、到处引用 ```go 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 和配置。 ## 自定义结构与占位符 公共体没有规定必须出现的字段,也可以把成功和失败分开: ```go 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 选项: ```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: ```go 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` 描述文本/文件等成功输出,详见 [迁移扩展](MIGRATION.md)。自定义回调错误协议仍使用上述 Go 入口,并由应用维护相应文档;生成器不会猜测自定义 Go 编码器的输出。如果在命名中间件里替换编码器,业务也必须同步维护文档。 框架内置路由的 404/405 使用应用默认格式;它们不属于某个 `.api` 分组。TLS 握手、畸形 HTTP、连接级过载等发生在进入应用前,仍按网络层协议处理。自定义编码器失败、panic 或返回非法状态/204 body 时,最终返回空 500,避免递归编码或泄露另一种响应体。 ## 运行示例与验证 ```sh 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](examples/contracts/responses.api)。验收测试会在临时目录生成真实项目,写入业务方法,执行已生成路由,检查数字错误码、原始 JSON、鉴权错误、路由覆盖与重复生成保护。 ## 错误目录与 HTTP 200 失败(v0.10.0-preview) 错误含义由项目的 `errors.Catalog` 或 `.api errorCatalog` 集中定义,返回字段/结构仍由本页的 response 模板控制。目录提供独立的逻辑标识、对外字符串/整数码、消息、HTTP 状态,使用 `WithErrors` 或生成路由绑定。详见 [统一错误目录](errors/README.md)。 `response.Result.IsError()` 综合明确失败标记、Err 和 HTTP 状态判断;HTTP 200 的业务错误不会使用成功码/成功提示,仍可选择 `errorBody`。新目录的公开码与提示优先于通用兜底;与同标识 Codes/Messages 冲突会拒绝,不会静默覆盖。没有目录的旧映射保持原有行为。 自定义 Encoder 可读取 `Code`(逻辑标识)、`BusinessCode`(目录对外码)、Message 与 Err,并返回项目要求的最终状态和媒体类型。不要仅通过 Status 判断成功;内部原因默认只供诊断,不应由 Encoder 直接序列化 Err。RawJSON 的成功输出保持原样,失败继续交给所选错误编码器。