夠浪的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 協議》,轉載必須註明作者和本文連結