go gin web 应用 实现 参数校验 自定义功能
1.准备工作
在进行 web
业务开发时,尽管 validator/v10
这个库已经为我们预置了很多校验的字段约束,难免有些场景下,我们想自定义一些规则进去,比如校验用户注册的密码,约束是含数字、大写字母、小写字母,特殊字符("*|&|$|#")的至少3种,常规的字段约束此时就不管用了,怎么办,当然可以通过自定义约束字段检验,自己操作的空间就大很多了。
根据 validator
库的相关规则,只要我们实现了如下函数:
// Func accepts a FieldLevel interface for all validation needs. The return
// value should be true when validation succeeds.
type Func func(fl FieldLevel) bool
就可以根据在定义 struct
时的 tag
字段注册到 validator
中。
怎样是不是很简单,接下来我们以简单的用户注册与用户登录的场景来实操下。
2.相关代码
目录结构
业务代码
项目启动
// main.go
package main
import (
"github.com/gin-gonic/gin"
v1 "go-gin-userDefinedValidator/api/v1"
)
func main() {
r := gin.Default()
r.GET("/hello", v1.Hello)
r.POST("/user/add", v1.AddUser)
r.POST("/auth/login", v1.Login)
_ = r.Run(":8080")
}
接口路由函数
// router.go
package v1
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/validator/v10"
"go-gin-userDefinedValidator/internal/request"
validate "go-gin-userDefinedValidator/internal/validator"
)
func Hello(ctx *gin.Context) {
ctx.JSON(http.StatusOK, gin.H{
"msg": "hello, this is from gin app server.",
})
}
func AddUser(ctx *gin.Context) {
var err error
var user request.UserParams
err = ctx.ShouldBindBodyWith(&user, binding.JSON)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{
"msg": err.Error(),
})
return
}
err = validate.Validate.Struct(user)
if err != nil {
details := make([]map[string]interface{}, 0)
for _, e := range err.(validator.ValidationErrors) {
m := make(map[string]interface{})
m["field"] = e.Field()
m["value"] = e.Value()
m["tag"] = e.Tag()
details = append(details, m)
}
ctx.JSON(http.StatusOK, gin.H{
"msg": "validate error",
"details": details,
})
return
}
ctx.JSON(http.StatusOK, gin.H{
"msg": fmt.Sprintf("Registered success, welcome user: %s", user.Username),
})
}
func Login(ctx *gin.Context) {
var err error
var login request.LoginParams
err = ctx.ShouldBindBodyWith(&login, binding.JSON)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{
"msg": err.Error(),
})
return
}
err = validate.Validate.Struct(login)
if err != nil {
details := make([]map[string]interface{}, 0)
for _, e := range err.(validator.ValidationErrors) {
m := make(map[string]interface{})
m["field"] = e.Field()
m["value"] = e.Value()
m["tag"] = e.Tag()
details = append(details, m)
}
ctx.JSON(http.StatusOK, gin.H{
"msg": "validate error",
"details": details,
})
return
}
ctx.JSON(http.StatusOK, gin.H{
"msg": fmt.Sprintf("user: %s, login success.", login.Username),
})
}
接口请求参数
// reqParams.go
package request
type UserParams struct {
Username string `json:"username" validate:"required,min=4,max=20"`
Password string `json:"password" validate:"required,passwd"`
ConfirmPassword string `json:"confirmPassword" validate:"required,eqfield=Password"`
Age int `json:"age,omitempty" validate:"omitempty,age"`
Gender int `json:"gender,omitempty" validate:"omitempty,oneof=1 0"`
}
type LoginParams struct {
Username string `json:"username" validate:"required,min=4,max=20"`
Password string `json:"password" validate:"required,passwd"`
}
自定义验证器
// myValidator.go
package validator
import (
"fmt"
"github.com/go-playground/validator/v10"
)
var Validate *validator.Validate
// 初始化注册自定义校验
func init() {
registerMyValidator()
}
func registerMyValidator() {
Validate = validator.New()
err := Validate.RegisterValidation("passwd", passwdFunc)
if err != nil {
fmt.Println("register passwd validator failed.")
return
}
err = Validate.RegisterValidation("age", ageFunc)
if err != nil {
fmt.Println("register age validator failed.")
return
}
fmt.Println("register user defined validator success.")
}
// user defined validator
// 定义密码验证
func passwdFunc(fl validator.FieldLevel) bool {
pwd := fl.Field().String()
if len(pwd) < 8 || len(pwd) > 20 {
return false
}
return true
}
// 定义age验证
func ageFunc(fl validator.FieldLevel) bool {
age := fl.Field().Int()
if age < 18 || age > 100 {
return false
}
return true
}
示例中,我们以简单的密码和年龄校验为例,实现了简单业务,下面看看相关测试。
3.测试
hello 接口
正常注册请求参数
多个字段参数问题
正常登陆
异常登陆
通过本示例的学习,相信你也可以轻松掌握自定义验证器的使用。
参考文章: