好大一棵树

够浪的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 协议》,转载必须注明作者和本文链接
pardon110
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
开发者 @ 社科大
文章
134
粉丝
24
喜欢
101
收藏
55
排名:106
访问:8.9 万
私信
所有博文
社区赞助商