gin框架配合go-playground/validator完成json的验证以及json验证错误翻译成中文

作者: adm 分类: go 发布时间: 2022-06-27

安装 go-playground/validator

go get github.com/go-playground/validator/v10

代码如下

package main

import (
	"fmt"
	"net/http"
	"reflect"
	"strings"

	"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"
	en_translations "github.com/go-playground/validator/v10/translations/en"
	zh_translations "github.com/go-playground/validator/v10/translations/zh"
)

type LoginForm struct {
	User     string `json:"user" binding:"required,min=3,max=10"`
	Password string `json:"password" binding:"required"`
}

type SignupForm struct {
	Age        uint8  `json:"age" binding:"gte=18,lte=130"`
	Name       string `json:"name" binding:"required,min=3"`
	Email      string `json:"email" binding:"required,email"`
	Password   string `json:"password" binding:"required,min=6"`
	RePassword string `json:"re_password" binding:"required,eqfield=Password"` // 跨字段验证
}

var trans ut.Translator

func removeTopStruct(fields map[string]string) map[string]string {
	// "LoginForm.user": "user长度不能超过10个字符"
	// 提取user,去掉LoginForm -- "user": "user长度不能超过10个字符"
	rsp := make(map[string]string)
	for field, err := range fields {
		rsp[field[strings.Index(field, ".")+1:]] = err
	}
	return rsp
}

func InitTrans(locale string) (err error) {
	// 修改gin框架中的validator引擎属性,实现定制
	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
		// 注册一个获取json的tag的自定义方法
		// "LoginForm.User": "User长度不能超过10个字符"
		// 将大写的User替换为json中定义的tag标签 -- "LoginForm.user": "user长度不能超过10个字符"
		v.RegisterTagNameFunc(func(field reflect.StructField) string {
			name := strings.SplitN(field.Tag.Get("json"), ",", 2)[0]
			if name == "-"{
				return ""
			}
			return name
		})

		zhT := zh.New() // 中文翻译器
		enT := en.New() // 英文翻译器
		// 第一个参数是备用的语言环境,后面的参数是应该支持的语言环境
		uni := ut.New(enT, zhT, enT)
		trans, ok = uni.GetTranslator(locale)
		fmt.Println(ok)
		if !ok {
			return fmt.Errorf("uni.GetTranslator(%s)", locale)
		}
		switch locale {
		case "en":
			en_translations.RegisterDefaultTranslations(v, trans)
		case "zh":
			zh_translations.RegisterDefaultTranslations(v, trans)
		default:
			en_translations.RegisterDefaultTranslations(v, trans)
		}
		return
	}
	return
}

func main() {
	if err := InitTrans("zh"); err != nil {
		fmt.Println("初始化翻译器错误")
		return
	}
	r := gin.Default()

	r.POST("/login", loginView)
	r.POST("/signup", signupView)

	r.Run()
}

func signupView(context *gin.Context) {
	var signupForm SignupForm
	if err := context.ShouldBind(&signupForm); err != nil {
		context.String(http.StatusBadRequest, err.Error())
		return
	}
	context.JSON(http.StatusOK, gin.H{"message": "注册成功"})
}

func loginView(context *gin.Context) {
	var loginForm LoginForm
	if err := context.ShouldBind(&loginForm); err != nil {
		errs, ok := err.(validator.ValidationErrors)
		if !ok{
			context.String(400, err.Error())
		}
		context.JSON(400, removeTopStruct(errs.Translate(trans)))
		return
	}
	context.JSON(http.StatusOK, gin.H{"message": "登录成功"})


}

自定义验证器不翻译解决办法

自定义验证器代码

// 自定义验证器
func ValidateMobile(fl validator.FieldLevel) bool {
	mobile := fl.Field().String()
	// 使用正则表达式判断mobile是否合法
	//pattern := "^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0,5-9]))\\d{8}$"
	pattern := `^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0,5-9]))\d{8}$`
	ok, _ := regexp.MatchString(pattern, mobile)
	if !ok{
		return false
	}
	return true
}
注册验证器,同时解决自定义验证器不翻译问题
	// 5. 注册验证器
	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
		_ = v.RegisterValidation("mobile", myvalidator.ValidateMobile)
		// 解决自定义验证器不翻译问题, 注意上面注册的自定义验证器mobile要和下面的保持一致
		_ = v.RegisterTranslation("mobile", global.Trans, func(ut ut.Translator) error {
			return ut.Add("mobile", "{0} 非法的手机号码!", true)
		}, func(ut ut.Translator, fe validator.FieldError) string {
			t, _ := ut.T("mobile", fe.Field())
			return t
		})
	}

如果觉得我的文章对您有用,请随意赞赏。您的支持将鼓励我继续创作!