0%

【Go】Gin 自定义参数校验和错误信息

Gin 使用 validator 库做数据校验,如下可以实现自定义校验、及自定义校验错误信息。

其中,自定义错误信息无法用于多层嵌套结构体,可能可以通过反射做到,但感觉在性能上很捉急。

代码

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package main

import (
"errors"
"fmt"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/validator/v10"
"net/http"
"reflect"
"strings"
)

type Body struct {
Name string `json:"name" binding:"oneof=vk vksir" err:"one of vk,vksir"`
Age int `json:"age" binding:"BodyAgeValidate" err:"only 18"`
}

func BodyAgeValidate(f validator.FieldLevel) bool {
value := f.Field().Int()
if value != 18 {
return false
}
return true
}

func GetValidateErr(obj any, rawErr error) error {
validationErrs, ok := rawErr.(validator.ValidationErrors)
if !ok {
return rawErr
}
var errString []string
for _, validationErr := range validationErrs {
field, ok := reflect.TypeOf(obj).FieldByName(validationErr.Field())
if ok {
if e := field.Tag.Get("err"); e != "" {
errString = append(errString, fmt.Sprintf("%s: %s", validationErr.Namespace(), e))
continue
}
}
errString = append(errString, validationErr.Error())
}
return errors.New(strings.Join(errString, "\n"))
}

func ping(c *gin.Context) {
b := Body{}
if err := c.ShouldBindJSON(&b); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"detail": GetValidateErr(b, err).Error()})
return
}
c.JSON(http.StatusOK, gin.H{})
}

func main() {
e := gin.Default()
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
v.RegisterValidation("BodyAgeValidate", BodyAgeValidate)
}
e.GET("/ping", ping)
e.Run("127.0.0.1:8080")
}

运行

1
2
3
4
5
6
curl --location --request GET 'http://127.0.0.1:8080/ping' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "v",
"age": 19
}'
1
2
3
{
"detail": "Body.Name: one of vk,vksir\nBody.Age: only 18"
}

参考文档: