在写 Golang 的 WebServer 时我就很难受,缺一个类似 Node.js 的 joi 的框架,有很多想写的规则写不出来。
在 Golang 社区中参数校验一般是用 https://github.com/go-playground/validator 或者 https://github.com/astaxie/beego/tree/develop/validation 这些类似的东西。
我在使用这类框架的时候遇到了两个问题:
- struct 的初始零值会干扰校验。比如 struct 上有个字段是 int,你在校验规则里写 required 的意思是期望客户端必须要传值上来,但是如果客户端传 0 上来校验就会失败,因为框架没法区分这个零值是客户端传上来的还是客户没写时反序列化默认赋的值。
- 拓展规则的语法难用。往往你要再很远的地方往 validator 对象上 register 一个 type,然后再在你的 struct 去用。如果这类规则是一次性的话,跟自己的代码逻辑离得的太远了可读性大大降低。
先给个 jio 的使用例子
package main
import (
"errors"
"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().Transform(func(ctx *jio.Context) {
if ctx.Value != "faceair" {
ctx.Abort(errors.New("you are not faceair"))
}
}).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)
}
我是这么解决这些问题的。
- 既然 struct 缺少字段是否存在的信息,我们就不把 struct 的反序列化跟参数校验搞到一起。在反序列化之前对数据做校验,我选择把这个过程放到一个中间件里,让校验无法通过代码就无需再往下走。
- 支持在定义 schema 的时候直接拓展规则,上文中 name 这个字段我就直接拓展了一个规则。
同时 jio 还能给参数设置默认值、帮你做类型转换(string 转 array 转 int 等等)、能选择校验规则的顺序、能根据其他字段的数据选择不同的校验规则等等,我觉得 jio 灵活性比任何一个校验框架都要好(也包括 joi)。
更多的使用文档可以直接查看 https://github.com/faceair/jio/blob/master/README.zh.md 欢迎大家多多使用,可以给 star 鼓励,也可以给与一些反馈我好继续迭代改进。