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:
    • 不能为空,必须是对象
    • 不允许同时存在nametitle
    • 包含以下元素:
      • 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

1 回复

更多关于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在设计上考虑了性能因素:

  1. Schema只需编译一次,可以重复使用
  2. 验证过程是惰性的,遇到第一个错误就会停止(除非配置为收集所有错误)
  3. 内置常用验证规则,避免重复编写验证逻辑

总结

jio是一个功能强大且易于使用的JSON Schema验证库,它提供了:

  • 简洁的链式API
  • 丰富的内置验证规则
  • 自定义验证支持
  • 条件验证
  • 数据转换
  • 详细的错误报告

对于需要验证JSON数据的Golang应用,jio是一个值得考虑的选择。它特别适合REST API的参数验证、配置文件的验证等场景。

更多详细用法可以参考jio的官方文档和示例代码。

回到顶部