Go語言結(jié)合validator包實現(xiàn)表單驗證
在現(xiàn)代 Web 開發(fā)中,表單驗證和錯誤處理是至關(guān)重要的環(huán)節(jié),尤其是在多語言環(huán)境下。
本文將通過一個實際的示例,演示如何使用 Go 語言的 Gin 框架結(jié)合 validator 包,實現(xiàn)高級的表單驗證功能,并且支持國際化(i18n)的錯誤信息提示。
背景與需求
假設我們正在開發(fā)一個用戶注冊功能,需要對用戶提交的信息進行嚴格的驗證。例如,用戶名不能為空、郵箱格式必須正確、密碼和確認密碼必須一致、用戶年齡應在合理范圍內(nèi)(如 1 到 130 歲),并且日期字段不能早于當前日期。除此之外,系統(tǒng)還需要根據(jù)用戶的語言偏好提供相應語言的錯誤提示信息。
代碼示例
我們將從以下幾個方面展開:
- 表單數(shù)據(jù)的結(jié)構(gòu)定義
- 表單驗證器的初始化與自定義
- 多語言支持的實現(xiàn)
- 處理表單提交與錯誤返回
package main import ( "fmt" "net/http" "reflect" "strings" "time" "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" "github.com/go-playground/locales/en" "github.com/go-playground/locales/zh" ut "github.com/go-playground/universal-translator" "github.com/go-playground/validator/v10" enTranslations "github.com/go-playground/validator/v10/translations/en" zhTranslations "github.com/go-playground/validator/v10/translations/zh" ) // 定義一個全局翻譯器 var trans ut.Translator
表單數(shù)據(jù)結(jié)構(gòu)定義
首先,我們定義用戶提交的表單數(shù)據(jù)結(jié)構(gòu) SignUpParam。這個結(jié)構(gòu)體中包含了用戶注冊時所需的各個字段,并通過結(jié)構(gòu)標簽(tags)指定了驗證規(guī)則。
type SignUpParam struct {
Age uint8 `json:"age" binding:"gte=1,lte=130"`
Name string `json:"name" binding:"required"`
Email string `json:"email" binding:"required,email"`
Password string `json:"password" binding:"required"`
RePassword string `json:"re_password" binding:"required,eqfield=Password"`
Date string `json:"date" binding:"required,datetime=2006-01-02,checkDate"`
}
Age字段必須在 1 到 130 歲之間。Name字段不能為空。Email字段必須是有效的電子郵件地址。Password和RePassword字段必須一致。Date字段需要使用自定義校驗方法checkDate,確保輸入日期晚于當前日期。
初始化與自定義表單驗證器
在 Gin 框架中,我們可以通過 binding.Validator.Engine() 獲取到內(nèi)置的驗證器,并對其進行自定義。
在下面的代碼中,我們完成了翻譯器的初始化,并注冊了自定義的標簽名稱和驗證方法。
func InitTrans(locale string) (err error) {
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
})
// 注冊結(jié)構(gòu)體級別的驗證函數(shù)
v.RegisterStructValidation(SignUpParamStructLevelValidation, SignUpParam{})
// 注冊自定義校驗方法
if err := v.RegisterValidation("checkDate", customFunc); err != nil {
return err
}
// 初始化多語言支持
zhT := zh.New()
enT := en.New()
uni := ut.New(enT, zhT, enT)
var ok bool
trans, ok = uni.GetTranslator(locale)
if !ok {
return fmt.Errorf("uni.GetTranslator(%s) failed", locale)
}
// 注冊語言翻譯
switch locale {
case "en":
err = enTranslations.RegisterDefaultTranslations(v, trans)
case "zh":
err = zhTranslations.RegisterDefaultTranslations(v, trans)
default:
err = enTranslations.RegisterDefaultTranslations(v, trans)
}
if err != nil {
return err
}
// 注冊自定義翻譯
if err := v.RegisterTranslation(
"checkDate",
trans,
registerTranslator("checkDate", "{0}必須晚于當前日期"),
translate,
); err != nil {
return err
}
return
}
return
}
實現(xiàn)自定義校驗邏輯
在上面的代碼中,我們自定義了兩個校驗函數(shù):
- customFunc:用于校驗日期是否晚于當前日期。
- SignUpParamStructLevelValidation:用于校驗兩個密碼字段是否一致。
func customFunc(fl validator.FieldLevel) bool {
date, err := time.Parse("2006-01-02", fl.Field().String())
if err != nil {
return false
}
return date.After(time.Now())
}
func SignUpParamStructLevelValidation(sl validator.StructLevel) {
su := sl.Current().Interface().(SignUpParam)
if su.Password != su.RePassword {
sl.ReportError(su.RePassword, "re_password", "RePassword", "eqfield", "password")
}
}
處理多語言錯誤提示
為了確保錯誤信息能夠根據(jù)用戶的語言偏好正確返回,我們注冊了一個自定義的翻譯函數(shù) registerTranslator,并在驗證失敗時使用該函數(shù)對錯誤信息進行翻譯。
// registerTranslator 為自定義字段添加翻譯功能
func registerTranslator(tag string, msg string) validator.RegisterTranslationsFunc {
return func(trans ut.Translator) error {
if err := trans.Add(tag, msg, false); err != nil {
return err
}
return nil
}
}
// translate 自定義字段的翻譯方法
func translate(trans ut.Translator, fe validator.FieldError) string {
msg, err := trans.T(fe.Tag(), fe.Field())
if err != nil {
panic(fe.(error).Error())
}
return msg
}
主程序邏輯
最后,我們在 Gin 中處理用戶的注冊請求。當用戶提交的數(shù)據(jù)驗證失敗時,系統(tǒng)會自動返回翻譯后的錯誤提示信息。
// removeTopStruct 去除字段名中的結(jié)構(gòu)體名稱標識
// refer from:https://github.com/go-playground/validator/issues/633#issuecomment-654382345
func removeTopStruct(fields map[string]string) map[string]string {
res := map[string]string{}
for field, err := range fields {
res[field[strings.Index(field, ".")+1:]] = err
}
return res
}
func main() {
// 初始化翻譯器
if err := InitTrans("zh"); err != nil {
fmt.Printf("初始化翻譯器失敗: %v\n", err)
return
}
r := gin.Default()
r.POST("/signup", func(c *gin.Context) {
var u SignUpParam
if err := c.ShouldBind(&u); err != nil {
errs, ok := err.(validator.ValidationErrors)
if !ok {
c.JSON(http.StatusOK, gin.H{"msg": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"msg": removeTopStruct(errs.Translate(trans))})
return
}
// 其他的一些業(yè)務邏輯操作……
c.JSON(http.StatusOK, gin.H{"msg": "success"})
})
err := r.Run(":8080")
if err != nil {
fmt.Printf("服務器運行失敗: %v\n", err)
}
}
總結(jié)
本文通過一個完整的示例,展示了如何在 Go 語言中使用 Gin 框架實現(xiàn)多語言的表單驗證。
我們不僅探討了基礎的驗證規(guī)則,還介紹了如何自定義驗證邏輯以及如何實現(xiàn)國際化的錯誤提示。這種方式使得我們的應用程序不僅在功能上更加強大,同時也能更好地適應全球化的需求。
到此這篇關(guān)于Go語言結(jié)合validator包實現(xiàn)表單驗證的文章就介紹到這了,更多相關(guān)Go validator表單驗證內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

