Golang使用validator進行數據校驗及自定義翻譯器


Golang使用validator進行數據校驗及自定義翻譯器

包下載:go get github.com/go-playground/validator/v10

一、概述

在接口開發經常會遇到一個問題是后端需要寫大量的繁瑣代碼進行數據校驗,所以就想着有沒有像前端校驗一樣寫規則進行匹配校驗,然后就發現了validator包,一個比較強大的校驗工具包下面是一些學習總結,詳細內容可以查看validator

二、操作符說明

標記 標記說明
, 多操作符分割
| 或操作
- 跳過字段驗證

三、常用標記說明

標記 標記說明
required 必填 Field或Struct validate:"required"
omitempty 空時忽略 Field或Struct validate:"omitempty"
len 長度 Field validate:"len=0"
eq 等於 Field validate:"eq=0"
gt 大於 Field validate:"gt=0"
gte 大於等於 Field validate:"gte=0"
lt 小於 Field validate:"lt=0"
lte 小於等於 Field validate:"lte=0"
eqfield 同一結構體字段相等 Field validate:"eqfield=Field2"
nefield 同一結構體字段不相等 Field validate:"nefield=Field2"
gtfield 大於同一結構體字段 Field validate:"gtfield=Field2"
gtefield 大於等於同一結構體字段 Field validate:"gtefield=Field2"
ltfield 小於同一結構體字段 Field validate:"ltfield=Field2"
ltefield 小於等於同一結構體字段 Field validate:"ltefield=Field2"
eqcsfield 跨不同結構體字段相等 Struct1.Field validate:"eqcsfield=Struct2.Field2"
necsfield 跨不同結構體字段不相等 Struct1.Field validate:"necsfield=Struct2.Field2"
gtcsfield 大於跨不同結構體字段 Struct1.Field validate:"gtcsfield=Struct2.Field2"
gtecsfield 大於等於跨不同結構體字段 Struct1.Field validate:"gtecsfield=Struct2.Field2"
ltcsfield 小於跨不同結構體字段 Struct1.Field validate:"ltcsfield=Struct2.Field2"
ltecsfield 小於等於跨不同結構體字段 Struct1.Field validate:"ltecsfield=Struct2.Field2"
min 最大值 Field validate:"min=1"
max 最小值 Field validate:"max=2"
structonly 僅驗證結構體,不驗證任何結構體字段 Struct validate:"structonly"
nostructlevel 不運行任何結構級別的驗證 Struct validate:"nostructlevel"
dive 向下延伸驗證,多層向下需要多個dive標記 [][]string validate:"gt=0,dive,len=1,dive,required"
dive Keys & EndKeys 與dive同時使用,用於對map對象的鍵的和值的驗證,keys為鍵,endkeys為值 map[string]string validate:"gt=0,dive,keys,eq=1\|eq=2,endkeys,required"
required_with 其他字段其中一個不為空且當前字段不為空 Field validate:"required_with=Field1 Field2"
required_with_all 其他所有字段不為空且當前字段不為空 Field validate:"required_with_all=Field1 Field2"
required_without 其他字段其中一個為空且當前字段不為空 Field `validate:"required_without=Field1 Field2"
required_without_all 其他所有字段為空且當前字段不為空 Field validate:"required_without_all=Field1 Field2"
isdefault 是默認值 Field validate:"isdefault=0"
oneof 其中之一 Field validate:"oneof=5 7 9"
containsfield 字段包含另一個字段 Field validate:"containsfield=Field2"
excludesfield 字段不包含另一個字段 Field validate:"excludesfield=Field2"
unique 是否唯一,通常用於切片或結構體 Field validate:"unique"
alphanum 字符串值是否只包含 ASCII 字母數字字符 Field validate:"alphanum"
alphaunicode 字符串值是否只包含 unicode 字符 Field validate:"alphaunicode"
alphanumunicode 字符串值是否只包含 unicode 字母數字字符 Field validate:"alphanumunicode"
numeric 字符串值是否包含基本的數值 Field validate:"numeric"
hexadecimal 字符串值是否包含有效的十六進制 Field validate:"hexadecimal"
hexcolor 字符串值是否包含有效的十六進制顏色 Field validate:"hexcolor"
lowercase 符串值是否只包含小寫字符 Field validate:"lowercase"
uppercase 符串值是否只包含大寫字符 Field validate:"uppercase"
email 字符串值包含一個有效的電子郵件 Field validate:"email"
json 字符串值是否為有效的 JSON Field validate:"json"
file 符串值是否包含有效的文件路徑,以及該文件是否存在於計算機上 Field validate:"file"
url 符串值是否包含有效的 url Field validate:"url"
uri 符串值是否包含有效的 uri Field validate:"uri"
base64 字符串值是否包含有效的 base64值 Field validate:"base64"
contains 字符串值包含子字符串值 Field validate:"contains=@"
containsany 字符串值包含子字符串值中的任何字符 Field validate:"containsany=abc"
containsrune 字符串值包含提供的特殊符號值 Field validate:"containsrune=☢"
excludes 字符串值不包含子字符串值 Field validate:"excludes=@"
excludesall 字符串值不包含任何子字符串值 Field validate:"excludesall=abc"
excludesrune 字符串值不包含提供的特殊符號值 Field validate:"containsrune=☢"
startswith 字符串以提供的字符串值開始 Field validate:"startswith=abc"
endswith 字符串以提供的字符串值結束 Field validate:"endswith=abc"
ip 字符串值是否包含有效的 IP 地址 Field validate:"ip"
ipv4 字符串值是否包含有效的 ipv4地址 Field validate:"ipv4"
datetime 字符串值是否包含有效的 日期 Field validate:"datetime"

四、標記使用注意

1、當搜索條件與特殊標記沖突時,如:逗號(,),或操作(|),中橫線(-)等則需要使用 UTF-8十六進制表示形式

例:

type Test struct {
   Field1 string  `validate:"excludesall=|"`    // 錯誤
   Field2 string `validate:"excludesall=0x7C"` // 正確.
}

2、可通過validationErrors := errs.(validator.ValidationErrors)獲取錯誤對象自定義返回響應錯誤

3、自定義校驗結果翻譯

// 初始化翻譯器
func validateInit() {
	zh_ch := zh.New()
	uni := ut.New(zh_ch)               // 萬能翻譯器,保存所有的語言環境和翻譯數據
	Trans, _ = uni.GetTranslator("zh") // 翻譯器
	Validate = validator.New()
	_ = zh_translations.RegisterDefaultTranslations(Validate, Trans)
	// 添加額外翻譯
	_ = Validate.RegisterTranslation("required_without", Trans, func(ut ut.Translator) error {
		return ut.Add("required_without", "{0} 為必填字段!", true)
	}, func(ut ut.Translator, fe validator.FieldError) string {
		t, _ := ut.T("required_without", fe.Field())
		return t
	})
}

五、使用示例

package main
import (
   "fmt"
   "github.com/go-playground/validator/v10"
)
// 實例化驗證對象
var validate = validator.New()
func main() {
   // 結構體驗證
   type Inner struct {
      String string `validate:"contains=111"`
   }
   inner := &Inner{String: "11@"}
   errs := validate.Struct(inner)
   if errs != nil {
      fmt.Println(errs.Error())
   }
   // 變量驗證
   m := map[string]string{"": "", "val3": "val3"}
   errs = validate.Var(m, "required,dive,keys,required,endkeys,required")
   if errs != nil {
      fmt.Println(errs.Error())
   }
}

六、gin框架中使用驗證翻譯器

1. 定義錯誤翻譯器

//validator.go
// 定義一個全局翻譯器
var trans ut.Translator

// InitTrans 初始化翻譯器
func InitTrans(locale string) (err error) {
	//修改gin框架中的Validator屬性,實現自定制
	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
		// 注冊一個獲取json tag的自定義方法
		v.RegisterTagNameFunc(func(fld reflect.StructField) string {
			name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
			if name == "-" {
				return ""
			}
			return name
		})

		zhT := zh.New() //中文翻譯器
		enT := en.New() //英文翻譯器

		// 第一個參數是備用(fallback)的語言環境
		// 后面的參數是應該支持的語言環境(支持多個)
		// uni := ut.New(zhT, zhT) 也是可以的
		uni := ut.New(enT, zhT, enT)

		// locale 通常取決於 http 請求頭的 'Accept-Language'
		var ok bool
		// 也可以使用 uni.FindTranslator(...) 傳入多個locale進行查找
		trans, ok = uni.GetTranslator(locale)
		if !ok {
			return fmt.Errorf("uni.GetTranslator(%s) failed", locale)
		}

		// 添加額外翻譯
		_ = v.RegisterTranslation("required_with", trans, func(ut ut.Translator) error {
			return ut.Add("required_with", "{0} 為必填字段!", true)
		}, func(ut ut.Translator, fe validator.FieldError) string {
			t, _ := ut.T("required_with", fe.Field())
			return t
		})
		_ = v.RegisterTranslation("required_without", trans, func(ut ut.Translator) error {
			return ut.Add("required_without", "{0} 為必填字段!", true)
		}, func(ut ut.Translator, fe validator.FieldError) string {
			t, _ := ut.T("required_without", fe.Field())
			return t
		})
		_ = v.RegisterTranslation("required_without_all", trans, func(ut ut.Translator) error {
			return ut.Add("required_without_all", "{0} 為必填字段!", true)
		}, func(ut ut.Translator, fe validator.FieldError) string {
			t, _ := ut.T("required_without_all", fe.Field())
			return t
		})

		// 注冊翻譯器
		switch locale {
		case "en":
			err = enTranslations.RegisterDefaultTranslations(v, trans)
		case "zh":
			err = zhTranslations.RegisterDefaultTranslations(v, trans)
		default:
			err = enTranslations.RegisterDefaultTranslations(v, trans)
		}
		return
	}
	return
}

func addValueToMap(fields map[string]string) map[string]interface{} {
	res := make(map[string]interface{})
	for field, err := range fields {
		fieldArr := strings.SplitN(field, ".", 2)
		if len(fieldArr) > 1 {
			NewFields := map[string]string{fieldArr[1]: err}
			returnMap := addValueToMap(NewFields)
			if res[fieldArr[0]] != nil {
				for k, v := range returnMap {
					res[fieldArr[0]].(map[string]interface{})[k] = v
				}
			} else {
				res[fieldArr[0]] = returnMap
			}
			continue
		} else {
			res[field] = err
			continue
		}
	}
	return res
}

// 去掉結構體名稱前綴
func removeTopStruct(fields map[string]string) map[string]interface{} {
	lowerMap := map[string]string{}
	for field, err := range fields {
		fieldArr := strings.SplitN(field, ".", 2)
		lowerMap[fieldArr[1]] = err
	}
	res := addValueToMap(lowerMap)
	return res
}

//handler中調用的錯誤翻譯方法
func ErrResp(err error) *gin.H {
	errs, ok := err.(validator.ValidationErrors)
	fmt.Println(reflect.TypeOf(err))
	if !ok {
		return &gin.H{
			"errCode": -1,
			"errMsg":  err.Error(), // 翻譯校驗錯誤提示
		}
	}
	return &gin.H{
		"errCode": -1,
		"errMsg":  removeTopStruct(errs.Translate(trans)), // 翻譯校驗錯誤提示
	}
}

2.使用

//router.go
//初始化翻譯器,這部分可放在main.go或router.go中
if err := util.InitTrans("zh"); err != nil {
		log.Fatalf("init trans failed, err:%v\n", err)
		return
}
//handler.go
//翻譯錯誤
func (h handler) Login(c *gin.Context) {
    req := models.LoginReq{}
    err := c.ShouldBindWith(&req, binding.JSON)
	if err != nil {
		c.JSON(http.StatusBadRequest, util.ErrResp(err)) //這里調用翻譯
		return
	}
}

參考文章:Go 使用validator進行后端數據校驗


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM