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 接口

正常注册请求参数

多个字段参数问题

正常登陆

异常登陆

通过本示例的学习,相信你也可以轻松掌握自定义验证器的使用。

参考文章:

posted on 2023-03-25 12:47  进击的davis  阅读(268)  评论(0编辑  收藏  举报

导航