Files
go-omni/RESPONSE.md

141 lines
8.0 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.
# 公共响应体与独立接口协议
这些功能从 `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、鉴权错误、路由覆盖与重复生成保护。