Go网络编程

Golang使用标签表达式校验结构体字段的有效性

2019-03-25  本文已影响4人  Andeya

一、背景

在服务的API接口层面,我们常常需要验证参数的有效性。
Golang中,大部分参数校验场景实际上是先将数据Bind到结构体,然后校验其字段值。

一般地,校验结构体字段值有如下两种实现方式。

  1. Case-By-Case 针对每个需校验的结构体字段分别写校验代码
    • 优点:自由灵活,适应所有场景
    • 缺点:重复且琐碎的码农工作,易使人厌烦
  2. 规则匹配,在结构体标签中设置预先支持的验证规则,如emailmax:100等形式
    • 优点:使用简单,不需要写琐碎的代码
    • 缺点:强依赖有限的规则,缺乏灵活性,无法满足复杂场景,如多字段关联验证等

思考:有没有一种方式,即简单易用(少写代码),又能满足各种复杂的校验场景?

答案是:有!结构体标签表达式 go-tagexpr 的出现,为我们提供了兼得鱼和熊掌的第三种选择。

二、认识 go-tagexpr

go-tagexpr 允许Gopher们在 struct tag 写表达式代码,并通过高性能的解释器计算其结果。

安装

go get -u github.com/bytedance/go-tagexpr

下面使用一个小示例,演示含有枚举、比较、字段关联的较复杂场景。

示例代码

import (
    "fmt"

    tagexpr "github.com/bytedance/go-tagexpr"
)

func ExampleTagexpr() {
    vm := tagexpr.New("te")
    type Meteorology struct {
        Season      string `te:"$=='spring'||$=='summer'||$=='autumn'||$=='winter'"`
        Weather     string `te:"$!='snowing' || (Season)$=='winter'"`
        Temperature int    `te:"{range:$>=-10 && $<38}{alarm:sprintf('Uncomfortable temperature: %v',$)}"`
    }
    m := &Meteorology{
        Season:      "summer",
        Weather:     "snowing",
        Temperature: 40,
    }
    r := vm.MustRun(m)
    fmt.Println(r.Eval("Season"))
    fmt.Println(r.Eval("Weather"))
    fmt.Println(r.Eval("Temperature@range"))
    fmt.Println(r.Eval("Temperature@alarm"))
    // Output:
    // true
    // false
    // false
    // Uncomfortable temperature: 40
}

代码诠释:

获取更多关于 go-expr 结构体标签表达式的语法知识 -> 查看这里

二、使用Validator校验

Validator 是有 go-expr 包提供的一个采用结构体标签表达式的参数校验组件。

主要特性

安装

go get -u github.com/bytedance/go-tagexpr

我们基于前面示例稍作修改,来演示如何使用validator校验结构体字段的有效性。

示例代码

import (
    "fmt"

    "github.com/bytedance/go-tagexpr/validator"
)

func ExampleValidator() {
    vd := validator.New("vd")
    type Meteorology struct {
        Season      string `vd:"$=='spring'||$=='summer'||$=='autumn'||$=='winter'"`
        Weather     string `vd:"$!='snowing' || (Season)$=='winter'"`
        Temperature int    `vd:"{@:$>=-10 && $<38}{msg:sprintf('Uncomfortable temperature: %v',$)}"`
        Contact     string `vd:"email($)"`
    }
    m := &Meteorology{
        Season:      "summer",
        Weather:     "rain",
        Temperature: 40,
        Contact:     "henrylee2cn@gmail.com",
    }
    err := vd.Validate(m)
    if err != nil {
        fmt.Println(err)
    }
    // Output:
    // Uncomfortable temperature: 40
}

代码诠释:

注册自己的校验函数

可能你已注意到 email($) 这个表达式,它是默认注册的一个函数表达式,用于验证邮箱的有效性。其实我们也可以定义自己通用的函数表达式,以便较少标签中的代码量,增加代码复用性。

下面以 email 函数的实现为例,演示如何注册自己的校验函数:

var pattern = "^([A-Za-z0-9_\\-\\.\u4e00-\u9fa5])+\\@([A-Za-z0-9_\\-\\.])+\\.([A-Za-z]{2,8})$"

emailRegexp := regexp.MustCompile(pattern)

validator.RegValidateFunc("email", func(args ...interface{}) bool {
    if len(args) != 1 {
        return false
    }
    s, ok := args[0].(string)
    if !ok {
        return false
    }
    return emailRegexp.MatchString(s)
}, true)

其中,validator.RegValidateFunc 的定义如下:

func RegValidateFunc(funcName string, fn func(args ...interface{}) bool, force ...bool) error

RegValidateFunc的force可选参数,表示是否强制覆盖已经注册了的同名函数。

结论:validator的使用方法非常简单、灵活且具有良好的扩展性,能够轻松满足各种复杂的验证场景。

获取更多关于 validator 校验器的语法知识 -> 查看这里

上一篇 下一篇

猜你喜欢

热点阅读