Skip to content

Vin中的validator数据校验

November 26, 2024
4 min read
782 words

gin框架中,默认集成了数据效验包 validator,首先定义接收请求参数的结构体,在结构体中 定义指定的效验规则。


基本使用

定义结构体

实例一个登录接口

go
package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

// Login 用户登录
func Login(ctx *gin.Context) {
	type TempData struct {
		Name     string `json:"username" binding:"required,max=2"`
		Password string `json:"password"`
	}
	temp := TempData{}
	if err := ctx.ShouldBind(&temp); err != nil {
		ctx.JSON(http.StatusBadRequest, ParamsNilError.WithMsg(err.Error()))
	}
}

请求结果

通httpClient请求接口

http
### 用户登录
// @no-log
POST http://localhost:8000/api/v1/user/login
Content-Type: application/json

{
  "username": "how2j"
}

响应结果

http
POST http://localhost:8000/api/v1/user/login

HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
Date: Tue, 26 Nov 2024 05:39:20 GMT
Content-Length: 129

{
  "code": -1,
  "status": false,
  "message": "Key: 'TempData.Name' Error:Field validation for 'Name' failed on the 'max' tag",
  "data": null
}
响应文件已保存。
> 由于禁用了文件登录,无法显示响应

Response code: 400 (Bad Request); Time: 7ms (7 ms); Content length: 129 bytes (129 B)

错误信息自定义

添加自定义错误信息tag

自定义tag规则: [tag]Msg: message

go
type TempData struct {
		Name     string `json:"username" binding:"required,max=2" requiredMsg:"用户名不能为空"`
		Password string `json:"password"`
	}

添加错误消息处理方法

创建/utils/request.go文件, ctx.ShouldBind(&temp)方法产生的error中,数据校验失败返回的是validator.ValidationErrors类型,根据这个类型进行错误消息的处理。

一、首先进行错误类型判断

go
// ValidateErrorMsg 消息类型
type ValidateErrorMsg map[string]string
func GetErrorMsg(err error, structure interface{}) string {
    // 如果err是validator.ValidationErrors类型,获取第一个错误信息
    if errors.As(err, &validationErrors) && len(validationErrors) > 0 {
        // ...processing
    }
}

二、 使用反射机制来获取结构体中自定义的tag信息

  1. 使用 reflect.TypeOf() 函数获取结构体的类型信息
  2. 通过FieldByName找到指定结构体字段信息
  3. filed.Tag.Get方法获取tag信息
go
// ValidateErrorMsg 消息类型
type ValidateErrorMsg map[string]string
func GetErrorMsg(err error, structure interface{}) string {
// 如果err是validator.ValidationErrors类型,获取第一个错误信息
if errors.As(err, &validationErrors) && len(validationErrors) > 0 {
s := reflect.TypeOf(structure)
errMsg := validationErrors[0]
filed, _ := s.FieldByName(errMsg.Field()) // 获取效验失败字段信息
errText := filed.Tag.Get(errMsg.Tag() + "Msg") // 获取自定义信息
}
}

三、完整代码

go
package utils

import (
	"errors"
	"github.com/go-playground/validator/v10"
	"reflect"
)

// ValidateErrorMsg 消息类型
type ValidateErrorMsg map[string]string

func GetErrorMsg(err error, structure interface{}) string {
	var validationErrors validator.ValidationErrors
	// 如果err是validator.ValidationErrors类型,获取第一个错误信息
	if errors.As(err, &validationErrors) && len(validationErrors) > 0 {
		s := reflect.TypeOf(structure)
		errMsg := validationErrors[0]
		filed, _ := s.FieldByName(errMsg.Field())
		errText := filed.Tag.Get(errMsg.Tag() + "Msg")
		// 如果没有自定义消息,返回错误成员本身的错误信息
		if errText == "" {
			return err.Error()
		}
		return errText
	}

	// 其他类型的错误直接返回错误信息
	return err.Error()
}

自定义效验规则

可以查看Gin-自定义验证器文档

创建validator.go文件

go
func InitValidator() {
    if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
        // 自定义验证器注册
        _ = v.RegisterValidation("mobile", validateMobile)
    }
}

// 校验手机号
func validateMobile(fl validator.FieldLevel) bool {
    mobile := fl.Field().String()
    // 手机号为空则不验证,这里只验证有值的
    if mobile == "" {
        return true
    }

    ok, _ := regexp.MatchString(`^1[3-9]\d{9}$`, mobile)

    return ok
}

main.go 文件中注册

go
func main() {
    // 注册验证器
    validator.InitValidator()
}