数据校验

数据校验是用于数据验证和错误收集的模块。数据校验可以用于前端输入数据校验,或者后端拿到下游响应校验。某些时候也可以用来验证数据库数据完整性。

这部分例子在Validation 例子(opens new window)

安装及测试

安装:

  1. go get github.com/beego/beego/v2/core/validation

测试:

  1. go test github.com/beego/beego/v2/core/validation

示例

直接使用示例:

  1. import (
  2. "github.com/beego/beego/v2/core/validation"
  3. "log"
  4. )
  5. type User struct {
  6. Name string
  7. Age int
  8. }
  9. func main() {
  10. u := User{"man", 40}
  11. valid := validation.Validation{}
  12. valid.Required(u.Name, "name")
  13. valid.MaxSize(u.Name, 15, "nameMax")
  14. valid.Range(u.Age, 0, 18, "age")
  15. if valid.HasErrors() {
  16. // 如果有错误信息,证明验证没通过
  17. // 打印错误信息
  18. for _, err := range valid.Errors {
  19. log.Println(err.Key, err.Message)
  20. }
  21. }
  22. // or use like this
  23. if v := valid.Max(u.Age, 140, "age"); !v.Ok {
  24. log.Println(v.Error.Key, v.Error.Message)
  25. }
  26. // 定制错误信息
  27. minAge := 18
  28. valid.Min(u.Age, minAge, "age").Message("少儿不宜!")
  29. // 错误信息格式化
  30. valid.Min(u.Age, minAge, "age").Message("%d不禁", minAge)
  31. }

用户也可以通过声明式的写法来表达某个字段需要遵守的校验规则,声明式写法是通过结构体的标签来实现的:

  • 验证函数写在 “valid” 的标签里
  • 各个函数之间用分号 “;” 分隔,分号后面可以有空格
  • 参数用括号 “()” 括起来,多个参数之间用逗号 “,” 分开,逗号后面可以有空格
  • 正则函数(Match)的匹配模式用两斜杠 “/“ 括起来
  • 各个函数的结果的 key 值为字段名.验证函数名

    import (

    1. "log"
    2. "strings"
    3. "github.com/beego/beego/v2/core/validation"

    )

    type user struct {

    1. Id int
    2. Name string `valid:"Required;Match(/^Bee.*/)"` // Name 不能为空并且以 Bee 开头
    3. Age int `valid:"Range(1, 140)"` // 1 <= Age <= 140,超出此范围即为不合法
    4. Email string `valid:"Email; MaxSize(100)"` // Email 字段需要符合邮箱格式,并且最大长度不能大于 100 个字符
    5. Mobile string `valid:"Mobile"` // Mobile 必须为正确的手机号
    6. IP string `valid:"IP"` // IP 必须为一个正确的 IPv4 地址

    }

    // 如果你的 struct 实现了接口 validation.ValidFormer // 当 StructTag 中的测试都成功时,将会执行 Valid 函数进行自定义验证 func (u user) Valid(v validation.Validation) {

    1. if strings.Index(u.Name, "admin") != -1 {
    2. // 通过 SetError 设置 Name 的错误信息,HasErrors 将会返回 true
    3. v.SetError("Name", "名称里不能含有 admin")
    4. }

    }

    func main() {

    1. valid := validation.Validation{}
    2. u := user{Name: "Beego", Age: 2, Email: "dev@web.me"}
    3. b, err := valid.Valid(&u)
    4. if err != nil {
    5. // handle error
    6. }
    7. if !b {
    8. // validation does not pass
    9. // blabla...
    10. for _, err := range valid.Errors {
    11. log.Println(err.Key, err.Message)
    12. }
    13. }

    }

需要注意的是,Valid方法是用户自定义验证方法,它接收两个参数:

  • 字段名字
  • 错误原因

在实现该接口的时候,只需要将错误信息写入validation.Validation.

StructTag 可用的验证函数:

  • Required 不为空,即各个类型要求不为其零值
  • Min(min int) 最小值,有效类型:int,其他类型都将不能通过验证
  • Max(max int) 最大值,有效类型:int,其他类型都将不能通过验证
  • Range(min, max int) 数值的范围,有效类型:int,他类型都将不能通过验证
  • MinSize(min int) 最小长度,有效类型:string slice,其他类型都将不能通过验证
  • MaxSize(max int) 最大长度,有效类型:string slice,其他类型都将不能通过验证
  • Length(length int) 指定长度,有效类型:string slice,其他类型都将不能通过验证
  • Alpha alpha 字符,有效类型:string,其他类型都将不能通过验证
  • Numeric 数字,有效类型:string,其他类型都将不能通过验证
  • AlphaNumeric alpha 字符或数字,有效类型:string,其他类型都将不能通过验证
  • Match(pattern string) 正则匹配,有效类型:string,其他类型都将被转成字符串再匹配(fmt.Sprintf(“%v”, obj).Match)
  • AlphaDash alpha 字符或数字或横杠 -_,有效类型:string,其他类型都将不能通过验证
  • Email 邮箱格式,有效类型:string,其他类型都将不能通过验证
  • IP IP 格式,目前只支持 IPv4 格式验证,有效类型:string,其他类型都将不能通过验证
  • Base64 base64 编码,有效类型:string,其他类型都将不能通过验证
  • Mobile 手机号,有效类型:string,其他类型都将不能通过验证
  • Tel 固定电话号,有效类型:string,其他类型都将不能通过验证
  • Phone 手机号或固定电话号,有效类型:string,其他类型都将不能通过验证
  • ZipCode 邮政编码,有效类型:string,其他类型都将不能通过验证

自定义验证

我们允许自己注册验证逻辑。使用方法:

  1. AddCustomFunc(name string, f CustomFunc) error

例如:

  1. type user struct {
  2. // ...
  3. Address string `valid:"ChinaAddress"`
  4. }
  5. func main() {
  6. _ = validation.AddCustomFunc("ChinaAddress", func(v *validation.Validation, obj interface{}, key string) {
  7. addr, ok := obj.(string)
  8. if !ok {
  9. return
  10. }
  11. if !strings.HasPrefix(addr, "China") {
  12. v.AddError(key, "China address only")
  13. }
  14. })
  15. // ...
  16. }

注意的是,AddCustomFunc并不是线程安全的。在我们的设计理念中,注册这种自定义的方法,应该在系统初始化阶段完成。在该阶段,应当不存在竞争问题。