使用validator.v9验证请求数据并转换成特定验证信息

在实际项目中,验证请求的数据并将验证的结果返回是一项必须的操作。 下面介绍一下使用gin框架并配合使用validator.v9来验证数据。

Note: 为了演示方便,将以一个不完善简单的用户注册流程来进行说明

  1. 建立用户结构体,并在Tag中添加验证条件,详细的验证规则,请参考包文档

    type User struct {
        Email    string `validate:"required,email"`
        Name     string `validate:"required,min=6,max=20"`
        Password string `validate:"required,min=6,max=20"`
    }
    
  2. 将数据绑定到结构体实例

    //Register 用户注册
    func Register(c *gin.Context) {
        u := db.User{}
        c.BindJSON(&u)
        ....
    }
    
  3. 导入验证包

    zhongwen "github.com/go-playground/locales/zh"
    ut "github.com/go-playground/universal-translator"
    validator "gopkg.in/go-playground/validator.v9"
    zh_translations "gopkg.in/go-playground/validator.v9/translations/zh"
    
  4. 创建包全局变量,在init方法中实例该结构体

    var (
        validate *validator.Validate
        trans    ut.Translator
    )
    
    func init() {
        //实例化需要转换的语言
        zh := zhongwen.New() 
        uni := ut.New(zh, zh)
        trans, _ = uni.GetTranslator("zh")
        validate = validator.New()
    
        //注册转换的语言为默认语言
        zh_translations.RegisterDefaultTranslations(validate, trans)
    }
    
  5. 验证绑定的模型数据

    //Register 用户注册
    func Register(c *gin.Context) {
        u := db.User{}
        c.BindJSON(&u)
        err := validate.Struct(u)
        if err != nil {
            errors := NewValidatorError(err, u.FieldTrans())
            ...
        }
    }
    
  6. User结构体添加字段对应的转换字段名称

    //ModelFieldTran 模型名称转换
    type ModelFieldTran map[string]string
    
    //FieldTrans 模型字段转换
    func (u User) FieldTrans() ModelFieldTran {
        m := ModelFieldTran{}
        m["Name"] = "用户名"
        m["Password"] = "用户密码"
        m["Email"] = "邮箱"
        return m
    }
    
  7. 转换错误信息

    //CommonError 错误格式
    type CommonError map[string]interface{}
    
    func NewValidatorError(err error, m db.ModelFieldTran) CommonError {
        res := CommonError{}
        errs := err.(validator.ValidationErrors)
        for _, e := range errs {
            transtr := e.Translate(trans)
            //将结构体字段转换map中的key为小写
            f := strings.ToLower(e.Field())
    
            //判断错误字段是否在命名中,如果在,则替换错误信息中的字段
            if rp, ok := m[e.Field()]; ok {
                res[f] = strings.Replace(transtr, e.Field(), rp, 1)
            } else {
                res[f] = transtr
            }
        }
        //返回错误信息
        return res
    }
    
  8. 设置响应结构体

    //ForumResp 响应结构体
    type ForumResp struct {
        Status  bool        `json:"status"`
        Data    interface{} `json:"data"`
        Msg     string      `json:"msg"`
        ErrCode int         `json:"err_code"`
        Errors  CommonError `json:"errors"`
    }
    
    //Success 成功响应
    func (f *ForumResp) Success(msg string, content interface{}) {
        f.Status = true
        f.ErrCode = 0
        f.Msg = msg
        f.Data = content
    }
    
    //Error 失败响应
    func (f *ForumResp) Error(code int, msg string, err CommonError) {
        f.Status = false
        f.ErrCode = code
        f.Msg = msg
        f.Errors = err
    }
    
  9. 返回响应

    //Register 用户注册
    func Register(c *gin.Context) {
        u := db.User{}
        c.BindJSON(&u)
        err := validate.Struct(u)
        if err != nil {
            errors := NewValidatorError(err, u.FieldTrans())
            resp := ForumResp{}
            resp.Error(http.StatusBadRequest, validateError, errors)
            c.JSON(500, resp)
            return
        }
    }
    
  10. 如果验证失败了,我们将会得到形式如下的错误信息

    {
        "status": false,
        "data": null,
        "msg": "验证失败",
        "err_code": 400,
        "errors": {
            "email": "邮箱为必填字段",
            "name": "用户名为必填字段"
        }
    }
    

参考资料