Gin - 自定义验证器

前言

在 Gin 中实现一个自定义验证器,本质上就是 实现一个函数 + 注册它 + 在结构体标签里使用它
Validator 底层用的是 go-playground/validator/v10,所以整个流程也完全遵循该库的规则。

1. 实现一个验证器函数

要写一个验证器,说白了就是写一个回调函数,这个函数最终会被 Gin 的 Validator 调用。
它的签名非常固定:

1
func(fl validator.FieldLevel) bool
  • flFieldLevel 对象,包含了关于当前字段的大量信息:
    • Field():字段的值(Value)
    • FieldName():字段名称
    • Param():标签里传进来的参数
    • GetTag():整个验证器 tag 字符串
    • ……

我们所要实现的验证器只需要根据自己的业务逻辑返回 true(通过)或 false(失败)。

示例:名称必须以 alex- 作为前缀

1
2
3
4
5
6
7
8
func nameValidator(fl validator.FieldLevel) bool {
if name, ok := fl.Field().Interface().(string); ok {
if strings.HasPrefix(name, "alex-") {
return true
}
}
return false
}

这里做了两件事:

  1. fl.Field().Interface() 拿到字段原始值
  2. 判断它是否是 string 并检查前缀

非常简单,逻辑也清晰。

2. 注册验证器

实现了验证器以后,就必须显式注册它,否则 Gin 永远不会调用它。

注册流程:

  1. 通过 binding.Validator.Engine() 拿到底层的 validator.Validate 实例
  2. 调用 RegisterValidation 注册
1
2
3
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
v.RegisterValidation("nameValid", nameValidator)
}
  • "nameValid" 是给验证器起的名字
  • nameValidator 是刚才实现的函数

之后就可以直接在结构体标签中使用 "nameValid" 了。

3. 在结构体上使用自定义验证器

用法和内置的 validator 标签完全一样,只要写在 binding 字段中:

1
2
3
type User struct {
Name string `form:"name" binding:"required,nameValid"`
}
  • required:必填
  • nameValid:你自定义的验证器

当用户发起请求时,Gin 的绑定器会自动调用验证器。

4. 完整代码

如下是一个完整示例,看起来更直观。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package main

import (
"strings"

"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/validator/v10"
)

type User struct {
Name string `form:"name" binding:"required,nameValid"`
}

func nameValidator(fl validator.FieldLevel) bool {
if name, ok := fl.Field().Interface().(string); ok {
if strings.HasPrefix(name, "alex-") {
return true
}
}
return false
}

func main() {
r := gin.Default()

// 注册自定义验证器
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
v.RegisterValidation("nameValid", nameValidator)
}

r.GET("/user", func(c *gin.Context) {
var user User
if err := c.ShouldBind(&user); err == nil {
c.JSON(200, gin.H{"message": "Validation passed!"})
} else {
c.JSON(400, gin.H{"error": err.Error()})
}
})

r.Run()
}

请求示例

可以看到,名称前缀没有 alex- 时请求失败,加上 alex- 请求成功。

Gin - 自定义验证器

官方示例

Gin 官方示例展示了一个 日期必须大于今天 的自定义验证器。
这个验证器稍微复杂一点,也会用到 Gin 的时间格式化能力。

时间格式 tag:time_format:"2006-01-02"

在 Go 中,时间格式化非常特别,它不是使用 YYYY-MM-DD,而是直接用这个固定日期:

1
2006-01-02 15:04:05

这是 Go 语言的一个历史设计,也是一种“记忆法”。

所以:

  • 想用 YYYY-MM-DD → 必须写成 2006-01-02
  • 想用 YYYY/MM/DD → 必须写 2006/01/02
  • 想解析时间戳 → 直接用 unix, unixmilli, unixmicro, unixnano

Gin 的 binding 只是把这些格式抽象成 tag。

实现日期可预约验证器

  • 如果日期早于今天 → 返回 false
  • 否则通过
1
2
3
4
5
6
7
8
9
10
func bookableDate(fl validator.FieldLevel) bool {
date, ok := fl.Field().Interface().(time.Time)
if ok {
today := time.Now()
if today.After(date) {
return false
}
}
return true
}

完整官方示例

  • gtfield=CheckIn:CheckOut 必须大于 CheckIn
  • time_format:"2006-01-02":binding 会自动解析 CheckInCheckOut
  • bookabledate 验证时间是否在未来
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package main

import (
"time"

"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/validator/v10"
)

type Booking struct {
CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
CheckOUt time.Time `form:"check_out" binding:"required,gtfield=CheckIn,bookabledate" time_format:"2006-01-02"`
}

func bookableDate(fl validator.FieldLevel) bool {
date, ok := fl.Field().Interface().(time.Time)
if ok {
today := time.Now()
if today.After(date) {
return false
}
}
return true
}

func main() {
r := gin.Default()

// 注册自定义验证器
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
v.RegisterValidation("bookabledate", bookableDate)
}

r.GET("/bookable", func(c *gin.Context) {
var b Booking
if err := c.ShouldBind(&b); err == nil {
c.JSON(200, gin.H{"message": "Booking dates are valid!"})
} else {
c.JSON(400, gin.H{"error": err.Error()})
}
})

r.Run()
}

Gin - 自定义验证器
https://blog.pangcy.cn/2025/11/23/后端编程相关/go/gin/Gin - 自定义验证器/
作者
子洋
发布于
2025年11月23日
许可协议