一、背景
- 需要对请求参数的校验,防止用户的恶意请求。
- 需要对代码错误码进行翻译,返回中文
- gin框架使用
github.com/go-playground/validator
进行参数校验,
- 在定义结构体时使用
binding
或 validate
, tag
标识相关校验规则
二、安装
$ go get github.com/go-playground/validator/v10
import "github.com/go-playground/validator/v10"
三、样例
package main
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
type RegisterRequest struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
Nickname string `json:"nickname" binding:"required"`
Email string `json:"email" binding:"required,email"`
Age uint8 `json:"age" binding:"gte=1,lte=120"`
}
func main() {
router := gin.Default()
router.POST("register", Register)
router.Run(":8080")
}
func Register(c *gin.Context) {
var r RegisterRequest
err := c.ShouldBindJSON(&r)
if err != nil {
c.JSON(http.StatusOK, gin.H{"msg": err.Error()})
return
}
c.JSON(http.StatusOK, "success")
}
四、常见约束
字符串约束
excludesall
:不包含参数中任意的 UNICODE 字符,例如excludesall=ab;
excludesrune
:不包含参数表示的 rune 字符,excludesrune=asong;
startswith
:以参数子串为前缀,例如startswith=hi;
endswith
:以参数子串为后缀,例如endswith=bye。
contains=
:包含参数子串,例如contains=email;
containsany
:包含参数中任意的 UNICODE 字符,例如containsany=ab;
containsrune
:包含参数表示的 rune 字符,例如`containsrune=asong;
excludes
:不包含参数子串,例如excludes=email;范围约束
范围约束的字段类型分为三种:
- 对于数值,我们则可以约束其值
- 对于切片、数组和map,我们则可以约束其长度
- 对于字符串,我们则可以约束其长度
tag介绍:
ne
:不等于参数值,例如ne=5;
gt
:大于参数值,例如gt=5;
gte
:大于等于参数值,例如gte=50;
lt
:小于参数值,例如lt=50;
lte
:小于等于参数值,例如lte=50;
oneof
:只能是列举出的值其中一个,这些值必须是数值或字符串,以空格分隔,如果字符串中有空格,将字符串用单引号包围,例如oneof=male female。
eq
:等于参数值,注意与len不同。对于字符串,eq约束字符串本身的值,而len约束字符串长度。例如eq=10;
len
:等于参数值,例如len=10;
max
:小于等于参数值,例如max=10;
min
:大于等于参数值,例如min=10
Fields
约束
eqfield
:定义字段间的相等约束,用于约束同一结构体中的字段。例如:eqfield=Password
eqcsfield
:约束统一结构体中字段等于另一个字段(相对),确认密码时可以使用,例如:eqfiel=ConfirmPassword
nefield
:用来约束两个字段是否相同,确认两种颜色是否一致时可以使用,例如:nefield=Color1
necsfield
:约束两个字段是否相同(相对)
常用约束
实践使用
注册初始化
var trans ut.Translator
func init() {
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
v.RegisterTagNameFunc(func(fld reflect.StructField) string {
var name string
for _, t := range []string{"json", "form", "uri", "query"} {
name = strings.SplitN(fld.Tag.Get(t), ",", 2)[0]
if name != "" {
if name == "-" {
return ""
}
return name
}
}
return name
})
trans, _ = ut.New(zh.New()).GetTranslator("zh")
_ = zhTranslations.RegisterDefaultTranslations(v, trans)
}
}
func Translate(err error) string {
var errs validator.ValidationErrors
if !errors.As(err, &errs) {
return e.InvalidArgs.Error()
} else {
return errs[0].Translate(trans)
}
}
翻译校验
type UsersRequest struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
Nickname string `json:"nickname" binding:"required"`
Email string `json:"email" binding:"required,email"`
Age uint8 `json:"age" binding:"gte=1,lte=120"`
}
func (u *userController) Users(c *gin.Context) {
var req types.UsersRequest
if err := c.ShouldBind(&req); err != nil {
zap.L().Error("request param error", zap.Error(err))
response.WithMsg(c, e.InvalidArgs, validation.Translate(err))
return
}
response.Success(c, nil)
}