golang JSON Schema验证工具插件库jio的使用
Golang JSON Schema验证工具插件库jio的使用
jio是一个简单高效的Golang JSON验证库,它可以在JSON反序列化前验证原始数据,避免结构体零值干扰等问题。
为什么使用jio?
Golang中的参数验证是一个常见问题:在结构体上定义标签不易扩展规则,手写验证代码会使逻辑代码变得冗长,而且结构体字段的初始零值也会干扰验证。
jio在反序列化前验证JSON原始数据,避免了这些问题。将验证规则定义为Schema易于阅读和扩展(灵感来自Hapi.js的joi库)。Schema中的规则可以按注册顺序验证,可以在规则之间使用上下文交换数据,甚至可以在单个规则中访问其他字段数据。
如何使用?
验证JSON字符串
package main
import (
"log"
"github.com/faceair/jio"
)
func main() {
data := []byte(`{
"debug": "on",
"window": {
"title": "Sample Widget",
"size": [500, 500]
}
}`)
_, err := jio.ValidateJSON(&data, jio.Object().Keys(jio.K{
"debug": jio.Bool().Truthy("on").Required(),
"window": jio.Object().Keys(jio.K{
"title": jio.String().Min(3).Max(18),
"size": jio.Array().Items(jio.Number().Integer()).Length(2).Required(),
}).Without("name", "title").Required(),
}))
if err != nil {
panic(err)
}
log.Printf("%s", data) // {"debug":true,"window":{"size":[500,500],"title":"Sample Widget"}}
}
上面的Schema定义了以下约束:
debug
:- 不能为空,验证结束时必须是布尔值
- 允许使用"on"字符串代替true
window
:- 不能为空,必须是对象
- 不允许同时存在
name
和title
- 包含以下元素:
title
:- 字符串,可以为空
- 如果不为空,长度在3到18之间
size
:- 数组,不能为空
- 有两个整数类型的子元素
使用中间件验证请求体
以chi框架为例,其他框架类似:
package main
import (
"io/ioutil"
"net/http"
"github.com/faceair/jio"
"github.com/go-chi/chi"
)
func main() {
r := chi.NewRouter()
r.Route("/people", func(r chi.Router) {
r.With(jio.ValidateBody(jio.Object().Keys(jio.K{
"name": jio.String().Min(3).Max(10).Required(),
"age": jio.Number().Integer().Min(0).Max(100).Required(),
"phone": jio.String().Regex(`^1[34578]\d{9}$`).Required(),
}), jio.DefaultErrorHandler)).Post("/", func(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
panic(err)
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusOK)
w.Write(body)
})
})
http.ListenAndServe(":8080", r)
}
jio.ValidateBody
的第二个参数用于验证失败时的错误处理。
使用中间件验证查询参数
package main
import (
"encoding/json"
"net/http"
"github.com/faceair/jio"
"github.com/go-chi/chi"
)
func main() {
r := chi.NewRouter()
r.Route("/people", func(r chi.Router) {
r.With(jio.ValidateQuery(jio.Object().Keys(jio.K{
"keyword": jio.String(),
"is_adult": jio.Bool().Truthy("true", "yes").Falsy("false", "no"),
"starts_with": jio.Number().ParseString().Integer(),
}), jio.DefaultErrorHandler)).Get("/", func(w http.ResponseWriter, r *http.Request) {
query := r.Context().Value(jio.ContextKeyQuery).(map[string]interface{})
body, err := json.Marshal(query)
if err != nil {
panic(err)
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusOK)
w.Write(body)
})
})
http.ListenAndServe(":8080", r)
}
注意查询参数的原始值是字符串,可能需要先转换值类型(例如jio.Number().ParseString()
或jio.Bool().Truthy(values)
)。
高级用法
工作流程
每个Schema由一系列规则组成,例如:
jio.String().Min(5).Max(10).Alphanum().Lowercase()
在这个例子中,String Schema有4个规则,分别是Min(5)
Max(10)
Alphanum()
Lowercase()
,将按顺序验证Min(5)
Max(10)
Alphanum()
Lowercase()
。如果一个规则验证失败,Schema的验证将停止并抛出错误。
为了提高代码可读性,这三个内置规则会先验证:
Required()
Optional()
Default(value)
例如:
jio.String().Min(5).Max(10).Alphanum().Lowercase().Required()
实际的验证顺序将是Required()
Min(5)
Max(10)
Alphanum()
Lowercase()
。
验证器上下文
工作流中的数据传递依赖于上下文,结构如下:
Type Context struct {
Value interface{} // 原始数据,你也可以重新赋值来改变结果
}
func (ctx *Context) Ref(refPath string) (value interface{}, ok bool) { // 引用其他字段数据
}
func (ctx *Context) Abort(err error) { // 终止验证并抛出错误
...
}
func (ctx *Context) Skip() { // 跳过后续规则
...
}
引用和优先级
在大多数情况下,规则只使用当前字段的数据,但有时需要与其他字段配合工作。例如:
{
"type": "ip", // 枚举值,`ip`或`domain`
"value": "8.8.8.8"
}
这个value
的验证规则由type
的值决定,可以写成:
jio.Object().Keys(jio.K{
"type": jio.String().Valid("ip", "domain").SetPriority(1).Default("ip"),
"value": jio.String().
When("type", "ip", jio.String().Regex(`^\d+\.\d+\.\d+\.\d+$`)).
When("type", "domain", jio.String().Regex(`^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0 -9]\.[a-zA-Z]{2,}$`)).Required(),
})
When
函数可以引用其他字段数据,如果成功,则将新的验证规则应用于当前数据。
如果你想在自定义规则中引用其他字段的数据,可以使用上下文上的Ref
方法。如果引用的数据是嵌套对象,引用字段的路径需要用.
连接。例如,如果你想引用people
对象下的name
,那么引用路径是people.name
:
{
"type": "people",
"people": {
"name": "faceair"
}
}
更多关于golang JSON Schema验证工具插件库jio的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang JSON Schema验证工具插件库jio的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
jio - Golang JSON Schema验证工具库
jio是一个轻量级的Golang JSON Schema验证库,它提供了一种简单直观的方式来验证JSON数据是否符合预定义的schema规则。下面我将详细介绍jio的使用方法。
安装
go get github.com/faceair/jio
基本用法
1. 定义Schema并验证数据
package main
import (
"fmt"
"github.com/faceair/jio"
)
func main() {
// 定义JSON Schema
schema := jio.Object().Keys(jio.K{
"name": jio.String().Min(3).Max(20).Required(),
"age": jio.Number().Integer().Min(18).Max(100),
"email": jio.String().Regex(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`),
"hobbies": jio.Array().Items(jio.String()),
})
// 要验证的JSON数据
data := map[string]interface{}{
"name": "Alice",
"age": 25,
"email": "alice@example.com",
"hobbies": []string{"reading", "swimming"},
}
// 验证数据
_, err := schema.Validate(data)
if err != nil {
fmt.Println("验证失败:", err)
} else {
fmt.Println("验证成功")
}
}
2. 常用验证规则
jio提供了丰富的验证规则:
// 字符串验证
jio.String().
Min(5). // 最小长度
Max(20). // 最大长度
Regex(`^[a-z]+$`). // 正则匹配
Alphanum(). // 只允许字母和数字
Required() // 必填字段
// 数字验证
jio.Number().
Min(0). // 最小值
Max(100). // 最大值
Integer(). // 必须是整数
Positive(). // 必须是正数
MultipleOf(5) // 必须是5的倍数
// 数组验证
jio.Array().
Min(1). // 最小长度
Max(10). // 最大长度
Items(jio.String()) // 数组元素类型
// 对象验证
jio.Object().
Keys(jio.K{ // 定义字段
"name": jio.String().Required(),
"age": jio.Number().Integer(),
}).
WithRequired("name") // 必填字段
3. 自定义验证器
你可以创建自定义验证规则:
func isEven() jio.Validator {
return jio.Number().Custom(func(value interface{}) error {
num := value.(float64)
if int(num)%2 != 0 {
return fmt.Errorf("must be even number")
}
return nil
})
}
// 使用自定义验证器
schema := jio.Object().Keys(jio.K{
"even_number": isEven().Required(),
})
4. 条件验证
jio支持条件验证:
schema := jio.Object().Keys(jio.K{
"type": jio.String().Valid("student", "teacher").Required(),
"grade": jio.String().When("type", "student", jio.String().Required()),
"subject": jio.String().When("type", "teacher", jio.String().Required()),
})
5. 错误处理
jio提供了详细的错误信息:
_, err := schema.Validate(data)
if err != nil {
if validationErr, ok := err.(*jio.ValidationError); ok {
for _, e := range validationErr.Errors {
fmt.Printf("字段: %s, 错误: %s\n", e.Field, e.Message)
}
}
}
高级用法
1. 转换数据
jio可以在验证的同时转换数据:
schema := jio.String().Trim().ToLower()
data := " Hello World "
result, _ := schema.Validate(data)
fmt.Println(result) // 输出: "hello world"
2. 验证嵌套结构
schema := jio.Object().Keys(jio.K{
"user": jio.Object().Keys(jio.K{
"name": jio.String().Required(),
"address": jio.Object().Keys(jio.K{
"city": jio.String().Required(),
"country": jio.String().Required(),
}),
}),
})
3. 异步验证
jio支持异步验证以提高性能:
schema := jio.Object().Keys(jio.K{
"name": jio.String().Required().Async(func(ctx *jio.Context) {
// 模拟异步操作
if ctx.Value == "admin" {
ctx.Abort("用户名不能是admin")
}
}),
})
data := map[string]interface{}{"name": "admin"}
_, err := schema.Validate(data)
fmt.Println(err) // 输出: "name: 用户名不能是admin"
性能考虑
jio在设计上考虑了性能因素:
- Schema只需编译一次,可以重复使用
- 验证过程是惰性的,遇到第一个错误就会停止(除非配置为收集所有错误)
- 内置常用验证规则,避免重复编写验证逻辑
总结
jio是一个功能强大且易于使用的JSON Schema验证库,它提供了:
- 简洁的链式API
- 丰富的内置验证规则
- 自定义验证支持
- 条件验证
- 数据转换
- 详细的错误报告
对于需要验证JSON数据的Golang应用,jio是一个值得考虑的选择。它特别适合REST API的参数验证、配置文件的验证等场景。
更多详细用法可以参考jio的官方文档和示例代码。