golang 参数校验方法设计

代码实现地址

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

  1. 设计一个struct, 存储要校验的值
  2. 实例一个 Rules, 保存这个struct 字段要校验的规则,比如:
    type Lg struct {
    	Name string
    	Age  int
    }
    var lgRules = Rules{"Name":{CheckEmpty()}, "Age":{Gt("18"), Lt("100")}}
    
  3. 然后我们设计一个校验方法,通过反射获取传入结构的字段,然后从规则 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

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值