请求验证#
请求验证#
Go 结构体标签用于为输入/输出结构体添加注释,这些注释会转换为 JSON Schema 以用于文档和验证。例如:
type Person struct {
Name string `json:"name" doc:"Person's name" minLength:"1" maxLength:"80"`
Age uint `json:"age,omitempty" doc:"Person's age" maximum:"120"`
}
字段命名#
支持标准的 json 标签,可以用于重命名字段。任何标记为 json:"-" 的字段将在 schema 中被忽略,就好像它不存在一样。
可选 / 必需#
字段是否可选/必需会自动确定,但可以使用以下逻辑根据需要覆盖:
- 初始假设所有字段均为必需。
- 如果字段有
omitempty,则为可选。 - 如果字段有
omitzero,则为可选。 - 如果字段有
required:"false",则为可选。 - 如果字段有
required:"true",则为必需。
指针不会影响可选/必需。无论结构体是用于请求输入还是响应输出,都适用相同的规则。一些示例:
type MyStruct struct {
// The following are all required.
Required1 string `json:"required1"`
Required2 *string `json:"required2"`
Required3 string `json:"required3,omitempty" required:"true"`
// The following are all optional.
Optional1 string `json:"optional1,omitempty"`
Optional2 string `json:"optional2,omitzero"`
Optional3 *string `json:"optional3,omitempty"`
Optional4 *string `json:"optional4,omitempty,omitzero"`
Optional5 string `json:"optional5" required:"false"`
}
注意
为什么在输入中使用 omitempty,而 Go 本身仅用于 marshalling?想象一个即将向您的 API 发送请求的客户端——它仍然必须被 marshalling 成 JSON(或类似格式)。您可以将输入结构体视为建模 API 客户端会产生的输出。
可空#
在许多语言(包括 Go)中,显式空值与未定义值之间几乎没有区别。如上所述,将字段标记为可选就足以支持两种情况。JavaScript 和 TypeScript 是此规则的例外,因为它们有明确的 null 和 undefined 值。
Huma 试图在 schema 简洁性、可用性和广泛兼容性之间取得平衡,同时兼顾 schema 正确性和广泛的语言支持,以实现端到端 API 工具链。为此,它在有限范围内支持字段可空性,未来变化可能会修改此默认行为,因为工具变得更兼容高级 JSON Schema 功能。
字段是否可空会自动确定,但可以使用以下逻辑根据需要覆盖:
- 初始假设没有字段可空
- 如果字段是指针(包括切片):
- 指向
boolean、integer、number、string:除非有omitempty,否则可空 - 指向
array:如果huma.DefaultArrayNullable为 true,则可空 - 指向
object:不可空,由于复杂性和许多工具对anyOf/oneOf的支持不佳
- 指向
- 如果字段有
nullable:"false",则不可空 - 如果字段有
nullable:"true":- 指向
boolean、integer、number、string、array:可空 - 指向
object:panic,表示当前不支持
- 指向
- 如果结构体有一个字段
_且nullable: true,则结构体可空,从而允许用户选择加入object而无需anyOf/oneOf的复杂性。
以下是一些示例:
// Make an entire struct (not its fields) nullable.
type MyStruct1 struct {
_ struct{} `nullable:"true"`
Field1 string `json:"field1"`
Field2 string `json:"field2"`
}
// Make a specific scalar field nullable. This is *not* supported for
// maps or structs. Structs *must* use the method above.
type MyStruct2 struct {
Field1 *string `json:"field1"`
Field2 string `json:"field2" nullable:"true"`
}
可空类型将生成类似 "type": ["string", "null"] 的类型数组,这具有广泛的兼容性,并且易于降级到 OpenAPI 3.0。还要记住,您始终可以提供 自定义 schema,如果内置功能不是您需要的。
注意
Go 中的切片如果本身为 nil 而非分配但为空,则 marshalling 成 JSON 为 null。这就是为什么切片默认可空的原因。有关更多信息,请参阅 Go JSON 包文档。
验证标签#
模型字段支持以下额外标签:
| 标签 | 描述 | 示例 |
|---|---|---|
doc |
描述字段 | doc:"Who to greet" |
format |
字段的格式提示 | format:"date-time" |
enum |
逗号分隔的可能值列表 | enum:"one,two,three" |
default |
默认值 | default:"123" |
minimum |
最小值(包含) | minimum:"1" |
exclusiveMinimum |
最小值(不包含) | exclusiveMinimum:"0" |
maximum |
最大值(包含) | maximum:"255" |
exclusiveMaximum |
最大值(不包含) | exclusiveMaximum:"100" |
multipleOf |
值必须是此值的倍数 | multipleOf:"2" |
minLength |
最小字符串长度 | minLength:"1" |
maxLength |
最大字符串长度 | maxLength:"80" |
pattern |
正则表达式模式 | pattern:"[a-z]+" |
patternDescription |
用于错误的模式描述 | patternDescription:"alphanum" |
minItems |
数组项的最小数量 | minItems:"1" |
maxItems |
数组项的最大数量 | maxItems:"20" |
uniqueItems |
数组项必须唯一 | uniqueItems:"true" |
minProperties |
对象属性的最小数量 | minProperties:"1" |
maxProperties |
对象属性的最大数量 | maxProperties:"20" |
example |
示例值 | example:"123" |
readOnly |
仅在响应中发送 | readOnly:"true" |
writeOnly |
仅在请求中发送 | writeOnly:"true" |
deprecated |
此字段已弃用 | deprecated:"true" |
hidden |
从文档中隐藏字段/参数 | hidden:"true" |
dependentRequired |
当字段存在时必需的字段 | dependentRequired:"one,two" |
内置字符串格式包括:
| 格式 | 描述 | 示例 |
|---|---|---|
date-time |
RFC3339 格式的日期和时间 | 2021-12-31T23:59:59Z |
date-time-http |
HTTP 格式的日期和时间 | Fri, 31 Dec 2021 23:59:59 GMT |
date |
RFC3339 格式的日期 | 2021-12-31 |
time |
RFC3339 格式的时间 | 23:59:59 |
email / idn-email |
电子邮件地址 | kari@example.com |
hostname |
主机名 | example.com |
ipv4 |
IPv4 地址 | 127.0.0.1 |
ipv6 |
IPv6 地址 | ::1 |
uri / iri |
URI | https://example.com |
uri-reference / iri-reference |
URI 引用 | /path/to/resource |
uri-template |
URI 模板 | /path/{id} |
json-pointer |
JSON 指针 | /path/to/field |
relative-json-pointer |
相对 JSON 指针 | 0/1 |
regex |
正则表达式 | [a-z]+ |
uuid |
UUID | 550e8400-e29b-41d4-a716-446655440000 |
默认值#
上面列出的 default 字段验证标签用于同时记录服务器端默认值的存在,以及自动让 Huma 为您设置该值。这对于可选但未提供时有默认值的字段很有用。
类似于标准库 JSON unmarshaler 的工作方式,推荐对于零值对您的应用程序有语义含义的标量类型使用指针。例如,如果您有一个默认为 true 的 bool 字段,则应使用 *bool 字段并将其默认值设置为 true。这样,如果字段未提供,将使用默认值。
如果您使用 bool 而非 *bool,则零值 false 会被默认值 true 覆盖,即使客户端明确发送了 false。
仅读和仅写#
请注意,readOnly 和 writeOnly 验证不由 Huma 强制执行,这些字段中的值也不会被 Huma 修改。它们纯粹用于文档目的,并允许您将结构体重用于输入和输出。
您需要注意确保仅读字段不被修改。由您决定是否忽略字段值、将其与例如数据存储中的现有值比较,或采取其他操作。这是为了启用更容易的数据往返的设计选择,例如读取带有仅读创建日期的 GET 响应,修改不同字段,然后通过 PUT 发送回服务器。服务器应忽略创建日期的存在和值,否则客户端必须进行潜在的多次修改,然后才能将数据发送回服务器。
仅写字段,如果存储在数据存储中,可以与 omitempty 结合使用,然后在处理程序中设置为空值,或通过数据存储查询或投影过滤掉。它们也可以完全不存储在数据存储中,但用于计算将要存储的字段值。
注意
如果仅写字段需要在请求中为必需,但同一结构体在响应中重用,您可以使用 json:"name,omitempty" 与 required:"true"。
严格 vs. 宽松字段验证#
默认情况下,Huma 对对象中允许的字段很严格,使用 additionalProperties: false JSON Schema 设置。这意味着如果客户端发送 schema 中未定义的字段,请求将被拒绝并报错。这有助于防止拼写错误和其他问题,并推荐用于大多数 API。
如果您需要允许额外字段,例如在使用第三方服务调用您的系统时,您只关心少数字段,则可以在结构体上使用 additionalProperties:"true" 字段标签,并将其分配给虚拟的 _ 字段。
type PartialInput struct {
_ struct{} `json:"-" additionalProperties:"true"`
Field1 string `json:"field1"`
Field2 bool `json:"field2"`
}
注意
使用 struct{} 是可选的,但高效。它用于避免为虚拟字段分配内存,因为空对象不需要空间。
高级验证#
在使用自定义 JSON Schema 时,即非从 Go 结构体生成的,可以利用更多验证规则。内置验证器会尊重以下 schema 字段:
not用于否定oneOf用于互斥输入anyOf用于匹配一个或多个allOf用于 schema 联合
有关更多信息,请参阅 huma.Schema。请注意,对于某些规则,使用自定义 解析器 可能更容易实现。
深入了解#
- 教程
- 您的第一个 API 包括字符串长度验证
- 参考
huma.Register注册新操作huma.Operation操作
- 外部链接