feat: declare API errors and authentication with developer tooling
This commit is contained in:
112
API.md
112
API.md
@@ -96,14 +96,119 @@ service user-api {
|
||||
- 可省略的 `syntax = "v1"`;`info` 中的 title、version 和描述性元数据。
|
||||
- 单独的 `type X { ... }` 与 `type (...)`;基础标量、指针、嵌套命名类型、切片、字符串键 map。嵌套对象只使用 JSON 字段。
|
||||
- 相对 `.api` 导入及 `import (...)`,重复导入去重;拒绝循环和越过入口目录的导入。单文件 1 MiB、总计 8 MiB、最多 64 个文件,限制导入/类型深度及 schema 展开规模。
|
||||
- 同一 service 名称的多个块;`@server` 的 group、prefix、middleware,group 可以写 `admin/user`。
|
||||
- 每个路由必须有 `@handler`;可选 `@doc "..."`;GET/POST/PUT/PATCH/DELETE/HEAD/OPTIONS。
|
||||
- 同一 service 名称的多个块;`@server` 的 group、prefix、middleware、auth、errors,group 可以写 `admin/user`。
|
||||
- 每个路由必须有 `@handler`;可选单行/多行 `@doc`、`@auth`、`@errors`;GET/POST/PUT/PATCH/DELETE/HEAD/OPTIONS。
|
||||
- 可省略请求或响应;有响应默认 200,无响应默认 204。Omni 扩展 `@status 201`、`@status 202`;204 不允许响应类型,HEAD 不允许响应体,GET/HEAD 不允许 JSON 请求体。
|
||||
- 每个请求字段只使用一个来源:`json`、`path`、`form`、`header`。本版 `form` 明确表示 URL query,不解析 multipart 或表单 body。query 切片用重复键;header/path 使用标量。
|
||||
- 来源标签中的 `optional` / `omitempty`、标量 `default`、标量 `options=a|b`、数值 `range=[1:100]`(支持开区间和单边界)。
|
||||
- `binding` 的 `required`、`min`、`max`、`len`、`email`。长度按 Unicode 字符或切片元素数;范围边界必须有限且位于 ±2^53 内。
|
||||
|
||||
暂不支持匿名嵌入、别名、递归类型、外部 Go 类型、自定义 JSON 编解码、`[]byte`、泛型、`json:",string"`、任意 validator 标签、go-zero 的 JWT/签名/SSE 声明,以及客户端 SDK 生成。认证请用命名中间件接入。`@doc` 块等未列出的语法也会明确报错。
|
||||
暂不支持匿名嵌入、别名、递归类型、外部 Go 类型、自定义 JSON 编解码、`[]byte`、泛型、`json:",string"`、任意 validator 标签、go-zero 的 JWT/签名/SSE 声明,以及客户端 SDK 生成。认证验证通过声明关联的命名中间件实现。未列出的语法会明确报错。
|
||||
|
||||
业务错误、具名鉴权、多行文档及下述开发命令从 CLI `v0.7.0-preview.1` 提供。
|
||||
|
||||
## 业务错误:声明、代码和文档共用一份定义
|
||||
|
||||
```go
|
||||
error (
|
||||
Unauthenticated 401 "UNAUTHENTICATED" "authentication required"
|
||||
Forbidden 403 "FORBIDDEN" "permission denied"
|
||||
UserNotFound 404 "USER_NOT_FOUND" "user not found"
|
||||
EmailTaken 409 "EMAIL_TAKEN" "email already in use"
|
||||
)
|
||||
```
|
||||
|
||||
也可写单条 `error EmailTaken 409 "EMAIL_TAKEN" "email already in use"`,或把错误目录放进相对导入的 `errors.api`。
|
||||
名称用 UpperCamelCase,HTTP 状态范围 400–599,错误码用大写字母/数字/下划线且全局唯一,message 是可公开返回的稳定提示。
|
||||
|
||||
`@server (errors: Unauthenticated)` 声明组级错误;路由 `@errors UserNotFound, EmailTaken` 追加到组级列表并去重。
|
||||
`@errors none` 清空继承的业务错误。未声明、重复声明或生成符号冲突会报告源码位置。
|
||||
|
||||
生成 `internal/apierrors/errorsGen.go`:
|
||||
|
||||
```go
|
||||
return nil, apierrors.EmailTaken()
|
||||
// 可用 apierrors.CodeEmailTaken 引用同一个错误码。
|
||||
```
|
||||
|
||||
每次调用返回新错误,避免并发修改共享错误对象。生成器不会自动把任意数据库错误转成业务错误;由 Service/适配层调用这些函数。
|
||||
内部 Cause 可能进入访问日志,敏感驱动报错应像 userService 示例一样先归类再转换。
|
||||
|
||||
OpenAPI 对每个状态生成标准响应结构和命名 examples,同一 409 下多个错误不会相互覆盖;纯业务状态还列出 code enum。
|
||||
默认框架错误状态与自动添加的鉴权 401 保留开放的 code 字段,容纳校验器/中间件实际错误。
|
||||
`x-omni-errors` 提供错误目录与路由关联。文档声明不拦截、改写或穷举运行时的所有错误。
|
||||
|
||||
## 鉴权:声明与路由注册一起变化
|
||||
|
||||
```go
|
||||
auth UserAuth (
|
||||
type: bearer
|
||||
middleware: Auth
|
||||
description: "Application access token"
|
||||
)
|
||||
|
||||
@server (
|
||||
group: user
|
||||
prefix: /api/v1
|
||||
auth: UserAuth
|
||||
errors: Unauthenticated
|
||||
)
|
||||
service user-api {
|
||||
@errors Forbidden, EmailTaken
|
||||
@handler createUser
|
||||
@status 201
|
||||
post /users (CreateUserReq) returns (UserResp)
|
||||
|
||||
@auth none
|
||||
@errors none
|
||||
@handler publicStatus
|
||||
get /status
|
||||
}
|
||||
```
|
||||
|
||||
`auth` 是具名契约,`middleware` 指向实际 hook;生成器把它放在普通 middleware 前面执行,并在注册任何路由前检查 hook 非空。
|
||||
新 hook 未实现时返回 501,已有手写 hook 保留。需要在 hook 中验证凭证、失效时间、签发者、权限等;声明不会自动实现登录或 JWT 校验。
|
||||
|
||||
- `type: bearer`:对应 HTTP Bearer,可选 `bearerFormat: JWT`,这只是文档提示。
|
||||
- `type: apiKey`:必须同时声明 `in: header` 或 `in: cookie` 和 `name`,例如 `X-API-Key` 或 `sessionId`。
|
||||
- `auth: UserAuth` 可在组级使用;路由 `@auth OtherAuth` 替换,`@auth none` 撤销继承的声明鉴权。普通 middleware 继续保留。
|
||||
- 不要再把该 auth 的 hook 重复写进普通 middleware。不支持 query token、多个方案 AND/OR、OAuth2 scopes 或隐含认证策略。
|
||||
- 被保护路由自动描述 401;403 和具体错误码由 `@errors` 明确声明。未声明 auth 的旧项目仍按其原有 middleware 执行。
|
||||
|
||||
文档生成 `components.securitySchemes` 与逐接口 `security`;显式公共接口使用空数组。
|
||||
结构遵循 [OpenAPI 3.0.3 Security Requirement](https://spec.openapis.org/oas/v3.0.3.html#security-requirement-object)。
|
||||
|
||||
## 文档和日常开发命令
|
||||
|
||||
保留单行 `@doc "摘要"`,也可以:
|
||||
|
||||
```go
|
||||
@doc (
|
||||
summary: "Create a user"
|
||||
description: "Writes user data and audit in the same transaction"
|
||||
tags: users, administration
|
||||
deprecated: false
|
||||
)
|
||||
```
|
||||
|
||||
未设置 tags 时使用 group 分组。`info` 的 description/desc 也会进入 OpenAPI;两者不能同时声明。
|
||||
|
||||
在生成项目目录内,不必重复输入 API 路径:
|
||||
|
||||
```sh
|
||||
go-omni api validate # 发现 manifest 中的入口并校验
|
||||
go-omni api inspect # 方法、路径、Handler、鉴权与业务错误总览
|
||||
go-omni api generate --dry-run # 预览新增/更新/移除,完全不写文件
|
||||
go-omni api generate # 实际生成,保留手写业务
|
||||
go-omni api generate --check # 只读检查,可用于 CI
|
||||
go-omni api export > openapi.json # 独立导出纯 JSON,无需启动服务
|
||||
go test ./...
|
||||
```
|
||||
|
||||
`--dry-run` 和 `--check` 不能组合。新项目预览也不创建目录;预览不是审批或锁定,实际生成会重新检查文件状态。
|
||||
在项目外可显式传 `--api`;生成时传 `--dir` 可定位其他项目。不会猜测多个 .api 中哪个是入口。
|
||||
路由总览的 `(undeclared)` 仅表示没有声明 auth;旧式中间件仍可能做认证。
|
||||
外发文档可用 export;项目内受生成器管理的 `docs/openapi.json` 请通过 generate 更新,以同步文件哈希。
|
||||
|
||||
## 参数语义
|
||||
|
||||
@@ -124,6 +229,7 @@ etc/config.yaml
|
||||
internal/settings/
|
||||
internal/servicecontext/ # 依赖初始化和释放
|
||||
internal/types/typesGen.go
|
||||
internal/apierrors/errorsGen.go # 声明错误后生成
|
||||
internal/handler/routesGen.go
|
||||
internal/handler/openapiGen.go
|
||||
internal/handler/<group>/handlersGen.go
|
||||
|
||||
Reference in New Issue
Block a user