好大一棵树
够浪的web路由超过十条,再使用官方库就不那么合适了。gin框架用了httprouter,另外比较好的是mux。
每个http谓词方法都会在合适的时候构建一棵压缩字典树,大多数框架路由请求用到了这种神操作。
请求校验
动波拳开路的箭头型代码
func register(req RegisterReq) error{
if len(req.Username) > 0 {
if len(req.PasswordNew) > 0 && len(req.PasswordRepeat) > 0 {
if req.PasswordNew == req.PasswordRepeat {
if emailFormatValid(req.Email) {
createUser()
return nil
} else {
return errors.New("invalid email")
}
} else {
return errors.New("password and reinput must be the same")
}
} else {
return errors.New("password and password reinput must be longer than 0")
}
} else {
return errors.New("length of username cannot be 0")
}
}
校验原理
码上说话,给待校验对象(结构体)打上标签,标签内带验证规则,通过反射解析规则
package main
import (
"fmt"
"reflect"
"regexp"
"strconv"
"strings"
)
// 重构波动拳开路的箭头代码
// 以请求校验代码示例,使用validator校验器原理
// Nested 结构体
type Nested struct {
Email string `validate:"email"`
}
// T 结构体
type T struct {
Age int `validate:"eq=10"`
Nested Nested
}
// 邮箱方法验证
func validateEmail(input string) bool {
if pass, _ := regexp.MatchString(
`^([\w\.\_]{2,10})@(\w{1,}).([a-z]{2,4})$`, input,
); pass {
return true
}
return false
}
// 树形验证数据成员
func validate(v interface{}) (bool, string) {
validateResult := true
errmsg := "success"
// 反射接口值数据类型
vt := reflect.TypeOf(v)
// 反射接口值数据值
vv := reflect.ValueOf(v)
// 遍历接口值结构体字段
for i := 0; i < vv.NumField(); i++ {
// 返回struct.v 字段对象
fieldVal := vv.Field(i)
// 获取字段标签对象并取对应的字符串值
tagContent := vt.Field(i).Tag.Get("validate")
// 获取字段值的在go中的原始类型种类
k := fieldVal.Kind()
switch k {
case reflect.Int:
// 断言数据值并返回相应的数据值
val := fieldVal.Int()
// 分割标签内容,获取结构标签中值
tagValStr := strings.Split(tagContent, "=")
tagVal, _ := strconv.ParseInt(tagValStr[1], 10, 64)
if val != tagVal {
errmsg = "validate int failed, tag is: " + strconv.FormatInt(
tagVal, 10,
)
validateResult = false
}
case reflect.String:
val := fieldVal.String()
tagValStr := tagContent
switch tagValStr {
case "email":
// 邮件方法验证
nestedResult := validateEmail(val)
if nestedResult == false {
errmsg = "validate mail failed, field val is: " + val
validateResult = false
}
}
// 内嵌结构体种类判断
case reflect.Struct:
// 进行尝试优先遍历,先获取该变量接口值,可从接口值获取类型及值及方法集信息
valInter := fieldVal.Interface()
// 进行递归
nestedResult, msg := validate(valInter)
if nestedResult == false {
validateResult = false
errmsg = msg
}
}
}
return validateResult, errmsg
}
func main() {
var a = T{Age: 9, Nested: Nested{Email: "abc@abc.com"}}
validateResult, errmsg := validate(a)
fmt.Println(validateResult, errmsg)
}
反射影响性能? 可以避免反射思路,使用Go内置的Parser对源代码进行扫描,然后根据结构体的定义生成校验代码
压缩字典树
每个节点上不只存储一个字母了,减少树的层数,同时因为每个节点上数据存储也比通常的字典树要多,所以程序的局部性较好(一个节点的path加载到cache即可进行多个字符的对比),从而对CPU缓存友好。
本作品采用《CC 协议》,转载必须注明作者和本文链接