Skip to content

操作#

操作是 Huma 的核心。它们将 HTTP 方法动词和资源路径映射到具有明确定义的输入和输出的处理函数。在查看由资源组成的 API 时,操作对应于这些资源上的 GETPOSTPUT 等方法,如以下示例所示:

graph TD
    subgraph Operations
        GET
        GET2[GET]
        POST
        PUT
        DELETE
    end

    API --> Resource1[Resource /items]
    API --> Resource2["Resource /users/{user-id}"]

    Resource1 --> POST
    Resource1 --> GET
    Resource1 --> DELETE
    Resource2 --> GET2
    Resource2 --> PUT

操作使用 huma.Register 函数创建:

huma.Register(api, huma.Operation{
	OperationID: "your-operation-name",
	Method:      http.MethodGet,
	Path:        "/path/to/resource/{id}",
	Summary:     "操作的简短描述",
}, func(ctx context.Context, input *YourInput) (*YourOutput, error) {
	// ... Implementation goes here ...
})

REST

如果遵循 REST-ish 约定,操作路径应为名词,如果返回多个项目则使用复数形式。好的示例:/notes/likes/users/{user-id}/videos/{video-id}/stats 等。Huma 不强制执行此约定,也不关心,因此也可以使用 RPC 风格的路径。使用最适合您和您的团队的方式。

OperationID

您知道吗?OperationID 用于在 Restish 中生成友好的 CLI 命令,并在生成 SDK 时使用!它应唯一、描述性强且易于输入。

Terminal
$ restish your-api your-operation-name --param=value ...

便捷方法#

如果您不想直接使用 huma.Operation 结构体,则提供了一系列便捷方法。以下是可用的方法:

  • huma.Get
  • huma.Post
  • huma.Put
  • huma.Patch
  • huma.Delete

这些方法等同于使用 huma.Register 并将 Method 字段设置为相应的 HTTP 方法,并且它们会根据路径为您生成操作 ID。例如:

code.go
huma.Get(api, "/things/{thing-id}", func(ctx context.Context, input *YourInput) (*YourOutput, error) {
    // ... Implementation goes here ...
})

在上面的示例中,生成的操作 ID 是 get-things-by-thing-id,摘要是 Get things by id。要自定义这些,请覆盖 huma.GenerateOperationID(method, path string, response any) 用于操作 ID,以及 huma.GenerateSummary(method, path string, response any) 用于摘要。

这使得入门变得容易,特别是如果您来自其他框架,并且当您需要设置操作的其他字段时,只需切换到使用 huma.Register

处理函数#

操作处理函数 始终 具有以下通用格式,其中 InputOutput 是开发者定义的自定义结构体,分别表示请求的全部内容(路径/查询/标头/ Cookie 参数和主体)和响应(标头和主体):

code.go
func(context.Context, *Input) (*Output, error)

操作有许多选项可用于配置 OpenAPI 设置,并且支持自定义扩展。有关更多详细信息,请参阅 huma.Operation 结构体。

输入和输出模型#

输入和输出 始终 是表示传入请求或传出响应的全部内容的结构体。这是为了使您的应用程序中的数据流更容易推理的 deliberate 设计决策。这也使共享代码以及生成文档和 SDK 变得更容易。

如果您的操作没有输入或输出,可以在注册时使用指向空结构体的指针 *struct{}

code.go
func(ctx context.Context, input *struct{}) (*struct{}, error) {
    // Successful response example, defaults to HTTP 204 No Content
    return nil, nil
}

请求流程#

传入 API 的请求在到达您的操作处理函数之前会经过多个步骤。以下图示显示了请求通过系统的流程,从请求输入(如路径/查询/标头参数和请求主体)开始,经过验证、操作处理函数,以及输出如何在响应中发送。

graph LR
    subgraph Inputs
        Path
        Query
        Header
        Body
        RawBody
    end

    subgraph Outputs
        Status
        Headers
        OutBody[Body]
    end

    Path --> Validate
    Query --> Validate
    Header --> Validate
    Body --> Unmarshal --> Validate
    Validate --> Resolve --> Operation
    RawBody -->|raw body input| Operation
    Operation --> Transform
    Transform --> Status
    Transform --> Headers
    Transform --> Marshal --> OutBody
    Operation -->|raw body output| OutBody

    style Operation stroke:#f9f,stroke-width:2px,stroke-dasharray: 5 5
Unmarshal

将请求主体的原始字节(例如 JSON)读取到 Go 结构中。

Validate

检查输入上的约束(例如 minimummaxLength 等)并报告失败。

Resolve

运行自定义验证代码并报告失败。

Operation

您操作的处理函数。业务逻辑在此处实现。它返回您的响应结构或错误。

Transform

在将结构化响应数据序列化为字节之前即时修改它。

Marshal

将结构化响应数据转换为字节(例如 JSON)。

继续阅读以了解每个步骤的工作原理。

深入探索#