go
语言中是没有类似java
一样的注解的方式来校验数据,但是可以在结构体中使用校验方法来校验(本人不是特别的喜欢这种方式,但是对于这门语言也只能这样)1、官网地址
https://github.com/go-playground/validator
2、安装依赖包
go get github.com/go-playground/validator
3、基础的使用参考官网
1、定义接收参数的结构体
type UserInfo struct {
Id string `validate:"uuid" json:"id"` //UUID 类型
Name string `validate:"checkName,required" json:"name"` // 自定义校验
Age uint8 `validate:"min=0,max=130,required" json:"age"` // 年龄
}
2、自定义方法
func checkNameFunc(f validator.FieldLevel) bool {
count := utf8.RuneCountInString(f.Field().String())
if count >= 2 && count <= 12 {
return true
} else {
return false
}
}
3、在init
函数中注册方法
var valildate *validator.Validate
func init() {
valildate = validator.New()
valildate.RegisterValidation("checkName", checkNameFunc)
}
4、在main
函数中使用
func main() {
router := gin.Default()
router.POST("/register", func(c *gin.Context) {
user := UserInfo{}
//或者var user = UserInfo
err := c.Bind(&user)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"code": 1,
"message": "请求参数错误",
})
return
}
err = valildate.Struct(user)
if err != nil {
// 输出校验错误 .(validator.ValidationErrors)是断言
for _, e := range err.(validator.ValidationErrors) {
fmt.Println("错误字段:", e.Field())
fmt.Println("错误的值:", e.Value())
fmt.Println("错误的tag:", e.Tag())
}
c.JSON(http.StatusOK, gin.H{
"code": 1,
"message": "数据校验错误",
})
return
}
fmt.Println(user, "接收的数据")
c.JSON(http.StatusOK, gin.H{
"code": 0,
"message": "请求成功",
})
})
router.Run(":8000")
}
1、对于多层结构体的数据校验使用dive
2、入参的结构体
type UserInfo struct {
Id string `validate:"uuid" json:"id"` //UUID 类型
Name string `validate:"checkName,required" json:"name"` // 自定义校验
Age uint8 `validate:"min=0,max=130,required" json:"age"` // 年龄
Address []Address `validate:"dive" json:"address"` // 收货地址
}
type Address struct {
Province string `json:"province" validate:"required"` // 省份
City string `json:"city" validate:"required"` // 市
County string `json:"county" validate:"required"` //县
Mobile string `json:"mobile" validate:"numeric,len=11"` // 手机号码
}
3、使用postman
提交数据
{
"name": "admin",
"age": 20,
"id": "3f7a783f-f9dc-4db8-9c6b-afe1d3f9b3f7",
"address": [
{
"province": "广东省",
"city":"深圳市",
"county": "宝安区",
"mobile": "18112345678"
}
]
}
4、别的和上面一样的结构
1、定义简单的注册结构体
type RegisterForm struct {
UserName string `json:"username" binding:"required,min=3,max=10"`
Password string `json:"password" binding:"required,min=3"`
RePassword string `json:"rePassword" binding:"required,eqfield=Password"`
}
2、常规的使用提示错误
{
"error": "Key: 'RegisterForm.RePassword' Error:Field validation for 'RePassword' failed on the 'eqfield' tag"
}
3、使用翻译成中文,可读性更加好,定义一个翻译器的方法
import (
"fmt"
"net/http"
"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"
)
var trans ut.Translator
//定义翻译的方法
func InitTrans(locale string) (err error) {
//修改gin框架中的validator引擎属性, 实现定制
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
zhT := zh.New() //中文翻译器
enT := en.New() //英文翻译器
//第一个参数是备用的语言环境,后面的参数是应该支持的语言环境
uni := ut.New(enT, zhT, enT)
trans, ok = uni.GetTranslator(locale)
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
}
4、使用翻译器
func main() {
if err := InitTrans("zh"); err != nil {
fmt.Println("初始化翻译器错误")
return
}
router := gin.Default()
router.POST("/register", func(c *gin.Context) {
var registerForm RegisterForm
if err := c.ShouldBind(®isterForm); err != nil {
fmt.Println(err.Error())
// 翻译错误
errs, ok := err.(validator.ValidationErrors)
if !ok {
c.JSON(http.StatusOK, gin.H{
"message": err.Error(),
})
}
// 将错误信息返回给前端
c.JSON(http.StatusOK, gin.H{
"error": errs.Translate(trans),
})
return
}
fmt.Println("注册的参数:", registerForm)
c.JSON(http.StatusOK, gin.H{
"code": 1,
"message": "成功",
"data": gin.H{
"username": registerForm.UserName,
"password": registerForm.Password,
},
})
})
router.Run(":8080")
}
{
"error": {
"RegisterForm.RePassword": "RePassword必须等于Password",
"RegisterForm.UserName": "UserName长度必须至少为3个字符"
}
}
5、移除多余的标签,让提示更加友好
func removeTopStruct(fileds map[string]string) map[string]string {
rsp := map[string]string{}
for field, err := range fileds {
rsp[field[strings.Index(field, ".")+1:]] = err
}
return rsp
}
if err := c.ShouldBind(®isterForm); err != nil {
fmt.Println(err.Error())
// 翻译错误
errs, ok := err.(validator.ValidationErrors)
if !ok {
c.JSON(http.StatusOK, gin.H{
"message": err.Error(),
})
}
c.JSON(http.StatusOK, gin.H{
"error": removeTopStruct(errs.Translate(trans)),
})
return
}
6、关于翻译成中文的完整代码
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"
)
var trans ut.Translator
type RegisterForm struct {
UserName string `json:"username" binding:"required,min=3,max=10"`
Password string `json:"password" binding:"required,min=3"`
RePassword string `json:"rePassword" binding:"required,eqfield=Password"`
}
func removeTopStruct(fileds map[string]string) map[string]string {
rsp := map[string]string{}
for field, err := range fileds {
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的自定义方法
v.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := strings.SplitN(fld.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)
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
}
router := gin.Default()
router.POST("/register", func(c *gin.Context) {
var registerForm RegisterForm
if err := c.ShouldBind(®isterForm); err != nil {
fmt.Println(err.Error())
// 翻译错误
errs, ok := err.(validator.ValidationErrors)
if !ok {
c.JSON(http.StatusOK, gin.H{
"message": err.Error(),
})
}
c.JSON(http.StatusOK, gin.H{
"error": removeTopStruct(errs.Translate(trans)),
})
return
}
fmt.Println("注册的参数:", registerForm)
c.JSON(http.StatusOK, gin.H{
"code": 1,
"message": "成功",
"data": gin.H{
"username": registerForm.UserName,
"password": registerForm.Password,
},
})
})
router.Run(":8080")
}
7、可以将上面这块封装成一个公共方法,以便在别的地方使用
package utils
import (
"fmt"
"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"
enTranslations "github.com/go-playground/validator/v10/translations/en"
zhTranslations "github.com/go-playground/validator/v10/translations/zh"
"reflect"
"strings"
)
var Trans ut.Translator
func init() {
if err := InitTrans("zh"); err != nil {
fmt.Println("初始化翻译器错误")
panic("初始化翻译器错误")
}
}
// RemoveTopStruct 移除多余的标签
func RemoveTopStruct(fileds map[string]string) map[string]string {
rsp := map[string]string{}
for field, err := range fileds {
rsp[field[strings.Index(field, ".")+1:]] = err
}
return rsp
}
// InitTrans 定义翻译的方法
func InitTrans(locale string) (err error) {
//修改gin框架中的validator引擎属性, 实现定制
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
//注册一个获取json的tag的自定义方法
v.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := strings.SplitN(fld.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)
if !ok {
return fmt.Errorf("uni.GetTranslator(%s)", locale)
}
switch locale {
case "en":
enTranslations.RegisterDefaultTranslations(v, Trans)
case "zh":
zhTranslations.RegisterDefaultTranslations(v, Trans)
default:
enTranslations.RegisterDefaultTranslations(v, Trans)
}
return
}
return
}
8、外部使用
package main
import (
"fmt"
"gin_stuty/01/utils"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"net/http"
)
type RegisterForm struct {
UserName string `json:"username" binding:"required,min=3,max=10"`
Password string `json:"password" binding:"required,min=3"`
RePassword string `json:"rePassword" binding:"required,eqfield=Password"`
}
func main() {
router := gin.Default()
router.POST("/register", func(ctx *gin.Context) {
var registerForm RegisterForm
if err := ctx.ShouldBind(®isterForm); err != nil {
fmt.Println(err.Error())
// 翻译错误
errs, ok := err.(validator.ValidationErrors)
if !ok {
ctx.JSON(http.StatusOK, gin.H{
"message": err.Error(),
})
}
// 使用封装的翻译方法
ctx.JSON(http.StatusOK, gin.H{
"error": utils.RemoveTopStruct(errs.Translate(utils.Trans)),
})
return
}
ctx.JSON(http.StatusOK, gin.H{
"code": 0,
"message": "成功",
})
})
fmt.Println(fmt.Sprintf("服务已经启动:localhost:9000"))
router.Run(":9000")
}
1、回顾上面的自定义翻译器
type RegisterForm1 struct {
//手机号码格式有规范可寻, 自定义validator,其中mobile是我们自定义的,官方是不带的
Mobile string `form:"mobile" json:"mobile" binding:"required,mobile"`
}
// ValidateMobile 自定义校验器
func ValidateMobile(f validator.FieldLevel) bool {
// 获取字段的值
mobile := f.Field().String()
// 使用正则来校验
if ok, _ := regexp.MatchString(`^1[3,5,7,8]\d{9}$`, mobile); ok {
return true
} else {
return false
}
}
2、注册自定义校验器来验证
func main() {
// 注册校验器
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
_ = v.RegisterValidation("mobile", ValidateMobile)
_ = v.RegisterTranslation("mobile", utils.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
})
}
router := gin.Default()
router.POST("/register", func(ctx *gin.Context) {
var registerForm RegisterForm1
if err := ctx.ShouldBindJSON(®isterForm); err != nil {
fmt.Println(err.Error())
// 翻译错误
errs, ok := err.(validator.ValidationErrors)
if !ok {
ctx.JSON(http.StatusOK, gin.H{
"message": err.Error(),
})
}
// 使用封装的翻译方法
ctx.JSON(http.StatusOK, gin.H{
"error": utils.RemoveTopStruct(errs.Translate(utils.Trans)),
})
return
}
ctx.JSON(http.StatusOK, gin.H{
"code": 0,
"message": "成功",
})
})
router.Run(":9000")
}
3、一般自定义校验器会统一存放到项目的validator
文件夹下,这里就不抽取出去