问题描述
在参数校验的时候我们一般会基于"github.com/go-playground/validator/v10"
这个库给结构体加标签实现校验参数,当参数校验错误的时候,他的提示一般是英文的,怎么自定义参数错误提示呢?跟着我一步步来
注册校验器
package initialize
import (
"ToDoList/global"
"ToDoList/model/request"
"ToDoList/util/validate"
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/locales/zh"
ut "github.com/go-playground/universal-translator"
v10 "github.com/go-playground/validator/v10"
zhTranslations "github.com/go-playground/validator/v10/translations/zh"
)
func RegisterValidator[T interface{}](fn v10.StructLevelFunc, model T) {
translator := zh.New()
universalTransator := ut.New(translator, translator)
global.GVA_TRANS, _ = universalTransator.GetTranslator("zh")
if v, ok := binding.Validator.Engine().(*v10.Validate); ok {
v.RegisterStructValidation(fn, model)
_ = zhTranslations.RegisterDefaultTranslations(v, global.GVA_TRANS)
}
}
type _Validator struct{}
func (receiver _Validator) InitValidator() {
RegisterValidator[request.Login](validate.UserLoginValidate.Validation, request.Login{})
}
var Validator = new(_Validator)
func (receiver *ginEngine) InitEngine() *gin.Engine {
r := gin.Default()
// 这里进行注册校验器!!
Validator.InitValidator()
return r
}
定义接口
我们定义一个接口规范,利于代码维护
package validate
import "github.com/go-playground/validator/v10"
type Validation interface {
CreateValidate() validator.StructLevelFunc
}
定义具体参数校验的方法
ReportError
函数第四个参数Param
将作为错误时消息提示。
package validate
import (
"ToDoList/model/request"
"github.com/go-playground/validator/v10"
"reflect"
)
type userLoginValidate struct{}
func (receiver userLoginValidate) CreateValidate() validator.StructLevelFunc {
return func(sl validator.StructLevel) {
user := sl.Current().Interface().(request.Login)
StructNotEmpty(sl, user, []string{})
if !VerifyPassword(8, 16, user.Password) {
sl.ReportError(reflect.ValueOf(user.Password), "", "", "", "密码要出现数字、大写字母、小写字母、特殊字符(.@$!%*#_~?&^),至少包含其中2种且长度在8-16之间")
}
}
}
var UserLoginValidate = new(userLoginValidate)
定义公共函数
1.定义一个翻译的函数
func CustomErrMessage(err error) string {
var result string
errors := err.(validator.ValidationErrors)
for _, err := range errors {
if err.Param() != "" {
result += err.Param() + ","
}
}
return result
}
2.判断结构体字段是否为空
// 结构体字段判断是否为空
func StructNotEmpty(sl validator.StructLevel, st interface{}, omitKeys []string) {
t := reflect.TypeOf(st)
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
if t.Kind() != reflect.Struct {
fmt.Println("必须传入结构体才能判断是否字段为空")
return
}
v := reflect.ValueOf(st) //获取reflect.Type类
for i := 0; i < v.NumField(); i++ {
if !slices.Contains(omitKeys, t.Field(i).Name) {
label := t.Field(i).Tag.Get("label")
value := v.Field(i).Interface().(string)
if len(value) == 0 {
sl.ReportError(t.Field(i), "", "", "", fmt.Sprintf("%s不能为空", label))
}
}
}
}
结构体标签
label
标签是结构体字段的中文翻译,以便于当结构体字段为空时StructNotEmpty
函数可以进行翻译。
type Login struct {
Username string `json:"username" binding:"required,min=4,max=16" label:"用户名"` // 用户名
Password string `json:"password" binding:"required,min=8,max=16" label:"密码"` // 密码
Captcha string `json:"captcha" binding:"required" label:"验证码"` // 验证码
CaptchaId string `json:"captchaId" binding:"required" label:"验证码ID"` // 验证码ID
}
返回结果
func (receiver *userApi) Login(c *gin.Context) {
// 1.绑定用户表单数据
var loginForm request.Login
// 2.检验用户登录表单
if err := c.ShouldBindJSON(&loginForm); err != nil {
response.Response.FailWithMessage(validate.CustomErrMessage(err), c)
return
}
}
结果展示
总结
- 先在Gin中注册验证器
- 定义校验函数并通过
sl validator.StructLevel
调用sl.ReportError
传入param
参数作为自定义错误 - 然后绑定结构体
var loginForm request.Login if err := c.ShouldBindJSON(&loginForm);
- 当绑定失败的时候把err传给
CustomErrMessage
函数CustomErrMessage
函数,将会把param的值作为错误结果 - 然后将
CustomErrMessage
函数返回结果给客户端即可