代码实现地址
https://github.com/wanmei002/goutil/blob/master/verify/verify.go
先看下设计好后的使用
type Lg struct {
Name string `json:"name"`
Age int `json:"age"`
}
// 判断 Name 是否为空,判断 Age 是否大于18 并小于 100
var lgs = verify.Rules{"Name": {verify.CheckEmpty()}, "Age": {verify.Gt("18"), verify.Lt("100")}}
var checkParam = Lg{Name:"zzh", Age:17}
err := verify.Verify(checkParam, lgs)
if err != nil {
c.JSON(http.StatusOK, gin.H{// 这里使用的 gin 框架输出
"code": -1,
"msg": err.Error(),
})
return
}
输出结果:
{
"code": -1,
"msg": "Age 小于 18"
}
思路
设计一个类型,保存检查参数规则
type Rules map[string][]string
设计支持的比较规则
我这里设计的是支持 是否为空
正则匹配
长度比较
大小比较
定义规则生成方法
// 是否为空
func CheckEmpty() string {
return "checkEmpty"
}
// 正则匹配
func RegExp(exp string) string {
return "regexp=" + exp
}
// 小于
func Lt(c string) string {
return "lt=" + c
}
这里简单列举了几个, 更多的请看代码文件
参数校验原理简单解析下
目前设计的只支持
struct
- 设计一个struct, 存储要校验的值
- 实例一个 Rules, 保存这个struct 字段要校验的规则,比如:
type Lg struct { Name string Age int } var lgRules = Rules{"Name":{CheckEmpty()}, "Age":{Gt("18"), Lt("100")}}
- 然后我们设计一个校验方法,通过反射获取传入结构的字段,然后从规则 map 中找到有没有对这个字段的校验规则,
如果有,则提取规则,进行比较
主要代码讲解
校验入口方法
func Verify(st interface{}, ruleMap Rules) error {
// 通过 TypeOf 获取 struct 的字段数量,字段名等
typ := reflect.TypeOf(st)
// 通过 ValueOf 获取 struct 的字段值等
val := reflect.ValueOf(st)
if val.Kind() != reflect.Struct {
return errors.New("expect struct")
}
// 获取结构体字段的数量
numF := typ.NumField()
for i := 0; i < numF; i++ {
// 获取字段名和字段值
fieldN := typ.Field(i)
fV := val.Field(i)
if len(ruleMap[fieldN.Name]) > 0 { // 说明有规则要处理
for _, v := range ruleMap[fieldN.Name] {
switch {
// 如果是要判断为空
case v == "checkEmpty":
if IsBlank(fV) {
return errors.New(fieldN.Name + " is empty")
}
......... // 这里省略了 正则比较等 case ,想了解的可以看看代码文件的代码
// 如果是要比较大小
case strings.Index(v, "=") >= 0:
if CompareVerify(fV, v) == false {
return errors.New(fieldN.Name + " " + v)
}
}
}
}
}
return nil
}
校验值是否为空
// 判断值是否是空白值
// 现在主要判断是否是 bool "" 0 ptr==nil intface==nil
func IsBlank(val reflect.Value) bool {
switch val.Kind() {// 获取值得类型
case reflect.String:
return val.String() == ""
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return val.Uint() == 0
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return val.Int() == 0
case reflect.Bool:
return !val.Bool()
case reflect.Ptr, reflect.Interface:
return val.IsNil()
}
// 零值比较
return reflect.DeepEqual(val.Interface(), reflect.Zero(val.Type()).Interface())
}
数值大小或长度的比较
// 比较 大小/长度
func CompareVerify(val reflect.Value, exp string) bool {
tp := val.Kind()
// 根据不同的类型进行不同的比较
tmp := strings.Split(exp, "=")
if len(tmp) < 2 {
pc, file, line, _ := runtime.Caller(0)
fnN := runtime.FuncForPC(pc)
log.Printf("file[%v] line[%v]; func name[%v]\n", file, line, fnN.Name())
return false
}
cp := tmp[0]
cV := tmp[1]
intV, _ := strconv.Atoi(cV)
switch tp {
case reflect.Slice, reflect.Map, reflect.Array, reflect.String:
return compare(val.Len(), cp, intV)// 对字符串 切片 map 等长度比较
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return compare(int(val.Int()), cp, intV)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return compare(int(val.Uint()), cp, intV)
}
return true
}
// 对数据进行 长度/大小 比较
func compare(len int, tp string, val int) bool {
switch tp {
case "lt":
return len < val
case "le":
return len <= val
case "eq":
return len == val
case "gt":
return len > val
case "ge":
return len >= val
}
return false
}
设计思路来源于: gin-vue-admin