詳解go-zero如何使用validator進行參數(shù)校驗
validator庫參數(shù)校驗若干實用技巧
在web開發(fā)中一個不可避免的環(huán)節(jié)就是對請求參數(shù)進行校驗,通常我們會在代碼中定義與請求參數(shù)相對應的模型(結(jié)構(gòu)體),借助模型綁定快捷地解析請求中的參數(shù)。本文就以 go-zero 框架的請求參數(shù)校驗為例,介紹一些validator庫的實用技巧。
go-zero框架使用github.com/go-playground/validator進行參數(shù)校驗,目前已經(jīng)支持github.com/go-playground/validator/v10了,我們需要在定義結(jié)構(gòu)體時使用 validate tag標識相關(guān)校驗規(guī)則,可以查看validator文檔查看支持的所有 tag。
安裝validator庫
go get github.com/go-playground/validator/v10
基本示例
首先來看go-zero框架內(nèi)置使用validator做參數(shù)校驗的基本示例。
在api層編寫Req時進行validator標簽的編輯
生成.go文件的時候type就會帶上相關(guān)標簽(types里面)
type (
TestReq {
Age int64 `json:"age" validate:"gte=1,lte=130"`
Name string `json:"name" validate:"required"`
Email string `json:"email" validate:"required,email"`
Password string `json:"password" validate:"required"`
RePassword string `json:"re_password" validate:"required,eqfield=Password"`
}
TestResp {
}
)
在handler層調(diào)用validator庫
這個是沒有使用validator前的handler
func TestApiHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.TestReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := lottery.NewTestApiLogic(r.Context(), svcCtx)
resp, err := l.TestApi(&req)
if err != nil {
result.ParamErrorResult(r, w, err)
} else {
result.HttpResult(r, w, resp, err)
}
}
}
使用validator校驗
err := validator.New().StructCtx(r.Context(), req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
后續(xù)配合模板,直接生成這段代碼即可,這樣handler層可以不必再編輯
翻譯校驗錯誤提示信息
validator庫本身是支持國際化的,借助相應的語言包可以實現(xiàn)校驗錯誤提示信息的自動翻譯。下面的示例代碼演示了如何將錯誤提示信息翻譯成中文,翻譯成其他語言的方法類似。
安裝翻譯相關(guān)包
go get github.com/go-playground/universal-translator
編寫validate的方法
func Validate(dataStruct interface{}) error {
zh_ch := zh.New()
validate := validator.New()
// 注冊一個函數(shù),獲取struct tag里自定義的label作為字段名
validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
if name == "-" {
return ""
}
return name
})
?
uni := ut.New(zh_ch)
trans, _ := uni.GetTranslator("zh")
// 驗證器注冊翻譯器
zh_translations.RegisterDefaultTranslations(validate, trans)
err := validate.Struct(dataStruct)
if err != nil {
for _, err := range err.(validator.ValidationErrors) {
return errors.New(err.Translate(trans))
}
}
return nil
}
編寫handler中的攔截(直接放模板里面去)
validateErr := translator.Validate(&req)
if validateErr != nil {
result.ParamErrorResult(r, w, validateErr)
return
}
自定義結(jié)構(gòu)體校驗方法
上面的校驗還是有點小問題,就是當涉及到一些復雜的校驗規(guī)則,比如re_password字段需要與password字段的值相等這樣的校驗規(guī)則,我們的自定義錯誤提示字段名稱方法就不能很好解決錯誤提示信息中的其他字段名稱了。
當我們測試re_password字段的時候,可以看到re_password字段的提示信息中還是出現(xiàn)了Password這個結(jié)構(gòu)體字段名稱。這有點小小的遺憾,畢竟自定義字段名稱的方法不能影響被當成param傳入的值。
{
"code": 100002,
"msg": "參數(shù)錯誤 ,re_password必須等于Password"
}
此時如果想要追求更好的提示效果,將上面的Password字段也改為和json tag一致的名稱,就需要我們自定義結(jié)構(gòu)體校驗的方法。
例如,我們?yōu)?code>SignUpParam自定義一個校驗方法如下:
// SignUpParamStructLevelValidation 自定義SignUpParam結(jié)構(gòu)體校驗函數(shù)
func SignUpParamStructLevelValidation(sl validator.StructLevel) {
su := sl.Current().Interface().(types.TestReq)
if su.Password != su.RePassword {
// 輸出錯誤提示信息,最后一個參數(shù)就是傳遞的param
sl.ReportError(su.RePassword, "re_password", "RePassword", "eqfield", "password")
}
}
并且刪掉原先的校驗
eqfield=Password // api中的這個校驗不要,重新生成.go文件
然后在初始化校驗器的函數(shù)中注冊該自定義校驗方法即可:
validate.RegisterStructValidation(lottery.SignUpParamStructLevelValidation, types.TestReq{})
最終再請求一次,看一下效果:
{
"code": 100002,
"msg": "參數(shù)錯誤 ,re_password必須等于password"
}
這一次re_password字段的錯誤提示信息就符合我們預期了。
自定義字段校驗方法
除了上面介紹到的自定義結(jié)構(gòu)體校驗方法,validator還支持為某個字段自定義校驗方法,并使用RegisterValidation()注冊到校驗器實例中。
接下來我們來為SignUpParam添加一個需要使用自定義校驗方法checkDate做參數(shù)校驗的字段Date。
TestReq {
Age int64 `json:"age" validate:"gte=1,lte=130"`
Name string `json:"name" validate:"required"`
Email string `json:"email" validate:"required,email"`
Password string `json:"password" validate:"required"`
RePassword string `json:"re_password" validate:"required"`
// 需要使用自定義校驗方法checkDate做參數(shù)校驗的字段Date
Date string `json:"date" validate:"required,datetime=2006-01-02,checkDate"`
}
其中datetime=2006-01-02是內(nèi)置的用于校驗日期類參數(shù)是否滿足指定格式要求的tag。 如果傳入的date參數(shù)不滿足2006-01-02這種格式就會提示如下錯誤:
{
"code": 100002,
"msg": "參數(shù)錯誤 ,date的格式必須是2006-01-02"
}
針對date字段除了內(nèi)置的datetime=2006-01-02提供的格式要求外,假設(shè)我們還要求該字段的時間必須是一個未來的時間(晚于當前時間),像這樣針對某個字段的特殊校驗需求就需要我們使用自定義字段校驗方法了。
首先我們要在需要執(zhí)行自定義校驗的字段后面添加自定義tag,這里使用的是checkDate,注意使用英文分號分隔開。
// customFunc 自定義字段級別校驗方法
func customFunc(fl validator.FieldLevel) bool {
date, err := time.Parse("2006-01-02", fl.Field().String())
if err != nil {
return false
}
if date.Before(time.Now()) {
return false
}
return true
}
定義好了字段及其自定義校驗方法后,就需要將它們聯(lián)系起來并注冊到我們的校驗器實例中。
// 注冊自定義結(jié)構(gòu)體字段校驗方法
if err := validate.RegisterValidation("checkDate", customFunc); err != nil {
return err
}
這樣,我們就可以對請求參數(shù)中date字段執(zhí)行自定義的checkDate進行校驗了。 我們發(fā)送如下請求測試一下,得到響應結(jié)果如下:
{
"code": 100002,
"msg": "參數(shù)錯誤 ,Key: 'TestReq.date' Error:Field validation for 'date' failed on the 'checkDate' tag"
}
這…自定義字段級別的校驗方法的錯誤提示信息很“簡單粗暴”,和我們上面的中文提示風格有出入,必須想辦法搞定它呀!
自定義翻譯方法
我們現(xiàn)在需要為自定義字段校驗方法提供一個自定義的翻譯方法,從而實現(xiàn)該字段錯誤提示信息的自定義顯示。
// 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
}
定義好了相關(guān)翻譯方法之后,我們在InitTrans函數(shù)中通過調(diào)用RegisterTranslation()方法來注冊我們自定義的翻譯方法。
// InitTrans 初始化翻譯器
func InitTrans(locale string) (err error) {
// ...liwenzhou.com...
// 注冊翻譯器
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
}
// 注意!因為這里會使用到trans實例
// 所以這一步注冊要放到trans初始化的后面
if err := v.RegisterTranslation(
"checkDate",
trans,
registerTranslator("checkDate", "{0}必須要晚于當前日期"),
translate,
); err != nil {
return err
}
return
}
return
}
這樣再次嘗試發(fā)送請求,就能得到想要的錯誤提示信息了。
{
"code": 100002,
"msg": "參數(shù)錯誤 ,date的格式必須是2006-01-02"
}
總結(jié)
本文總結(jié)的go-zero框架中validator的使用技巧同樣也適用于直接使用validator庫,區(qū)別僅僅在于我們配置的是go-zero框架中的校驗器還是由validator.New()創(chuàng)建的校驗器。同時使用validator庫確實能夠在一定程度上減少我們的編碼量,但是它不太可能完美解決我們所有需求,所以你需要找到兩者之間的平衡點。
以上就是詳解go-zero如何使用validator進行參數(shù)校驗的詳細內(nèi)容,更多關(guān)于go validator參數(shù)校驗的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Go使用github.com/pdfcpu/pdfcpu處理PDF的操作指南
在 Go 生態(tài)中,處理 PDF 文件一直是一個比較復雜的需求,pdfcpu 是一個功能強大的 Go 開源庫,專門用于 PDF 的解析與處理,本文給大家介紹了Go使用github.com/pdfcpu/pdfcpu處理PDF的操作指南,需要的朋友可以參考下2026-03-03
go?sync包中的互斥鎖Mutex和等待組WaitGroup使用詳解
這篇文章主要為大家介紹了go?sync包中的互斥鎖Mutex和等待組WaitGroup使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-08-08

