自定义验证
内置验证器
Huma 提供了许多基于 JSON Schema 的内置验证器。这些验证器支持大多数基本用例,优先于编写自己的代码进行相同的检查。
内置验证器包括 minimum、maximum、multipleOf、minLength、maxLength、pattern、enum、minItems、maxItems 等。例如:
code.go type MyInput struct {
ThingID string `path:"thing-id" maxLength:"12"`
Tag string `query:"tag" enum:"foo,bar,baz"`
Sales uint `query:"sales" maximum:"1000"`
}
有关所有可用验证器的信息,请参阅 请求验证 。某些验证器会自动添加,例如上面的 uint 在生成 JSON Schema 时会自动使用 minimum:"0"。
解析器
有时,您需要进行比内置验证器更复杂的验证。例如,您可能想要验证字段值不是某些已知的不良值。在这种情况下,您可以使用解析器。解析器是附加到输入的方法,在验证期间调用,并可以返回错误。
code.go type MyInput struct {
ThingID string `path:"thing-id"`
}
func ( i * MyInput ) Resolve ( ctx huma . Context ) [] error {
if i . ThingID == "bad" {
return [] error { & huma . ErrorDetail {
Location : "path.thing-id" ,
Message : "Thing ID cannot be 'bad'" ,
Value : i . ThingID ,
}}
}
return nil
}
var _ huma . Resolver = ( * MyInput )( nil )
有关更多详细信息,请参阅 解析器 。
示例
以下是使用解析器为参数和主体字段提供额外验证的示例,以及如何返回详尽的错误。
code.go // 此示例展示了如何使用解析器为参数和主体字段提供额外验证,
// 以及如何返回详尽的错误。
//
// # 返回七个错误的示例调用
// restish put :8888/count/3?count=15 -H Count:-3 count:9, nested.subCount: 6
//
// # 示例成功
// restish put :8888/count/1 count:2, nested.subCount: 4
package main
import (
"context"
"fmt"
"net/http"
"github.com/danielgtaylor/huma/v2"
"github.com/danielgtaylor/huma/v2/adapters/humachi"
"github.com/danielgtaylor/huma/v2/humacli"
"github.com/go-chi/chi/v5"
)
// CLI 的选项。
type Options struct {
Port int `doc:"Port to listen on." short:"p" default:"8888"`
}
// 创建一个带有附加验证的新输入类型。
type IntNot3 int
// Resolve 由 Huma 调用以验证输入。Prefix 是当前路径,例如 `path.to[3].field`,
// 如 `query.count` 或 `body.nested.subCount`。
// 解析器也可以附加到结构体,以提供跨多个字段组合的验证,例如“如果设置了 foo,则 bar 必须是 foo 值
// 的倍数”。在这种情况下,使用 `prefix.With("bar")`。
func ( i IntNot3 ) Resolve ( ctx huma . Context , prefix * huma . PathBuffer ) [] error {
if i != 0 && i % 3 == 0 {
return [] error { & huma . ErrorDetail {
Location : prefix . String (),
Message : "Value cannot be a multiple of three" ,
Value : i ,
}}
}
return nil
}
// 确保我们的解析器符合预期的接口。
var _ huma . ResolverWithPath = ( * IntNot3 )( nil )
func main () {
// 创建 CLI,传递一个函数,在解析自定义选项后调用。
cli := humacli . New ( func ( hooks humacli . Hooks , options * Options ) {
router := chi . NewMux ()
api := humachi . New ( router , huma . DefaultConfig ( "My API" , "1.0.0" ))
// 注册问候操作。
huma . Register ( api , huma . Operation {
OperationID : "put-count" ,
Summary : "Put a count of things" ,
Method : http . MethodPut ,
Path : "/count/{count}" ,
}, func ( ctx context . Context , input * struct {
PathCount IntNot3 `path:"count" example:"2" minimum:"1" maximum:"10"`
QueryCount IntNot3 `query:"count" example:"2" minimum:"1" maximum:"10"`
HeaderCount IntNot3 `header:"Count" example:"2" minimum:"1" maximum:"10"`
Body struct {
Count IntNot3 `json:"count" example:"2" minimum:"1" maximum:"10"`
Nested * struct {
SubCount IntNot3 `json:"subCount" example:"2" minimum:"1" maximum:"10"`
} `json:"nested,omitempty"`
}
}) ( * struct {}, error ) {
fmt . Printf ( "Got input: %+v\n" , input )
return nil , nil
})
// 告诉 CLI 如何启动您的路由器。
hooks . OnStart ( func () {
// 启动服务器
http . ListenAndServe ( fmt . Sprintf ( ":%d" , options . Port ), router )
})
})
// 运行 CLI。当不传递命令时,它会启动服务器。
cli . Run ()
}