从结构标记验证返回自定义错误消息

Joh*_*ohn 10 validation go go-gin

我将 Go 1.17 与 Gin 一起使用,我想在将数据发送到数据库之前实现结构验证。我从Gin 文档中获取了示例。

在结构中,我们可以声明不同的标签来验证字段,如下所示:

type User struct {
    FirstName      string `json:"first_name" binding:"required"`
    LastName       string `json:"last_name" binding:"required"`
    Age            uint8  `json:"age" binding:"gte=0,lte=130"`
    Email          string `json:"email" binding:"required,email"`
    FavouriteColor string `json:"favourite_color" binding:"iscolor"`
}
Run Code Online (Sandbox Code Playgroud)

在处理程序中,我可以捕获如下错误:

var u User
if err := c.ShouldBindWith(&u, binding.Query); err == nil {
    c.JSON(http.StatusOK, gin.H{"message": "Good Job"})
} else {
    c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
Run Code Online (Sandbox Code Playgroud)

错误消息将是:

{
    "error": "Key: 'User.FirstName' Error:Field validation for 'FirstName' failed on the 'required' tag\nKey: 'User.LastName' Error:Field validation for 'LastName' failed on the 'required' tag\nKey: 'User.Email' Error:Field validation for 'Email' failed on the 'required' tag\nKey: 'User.FavouriteColor' Error:Field validation for 'FavouriteColor' failed on the 'iscolor' tag"
}
Run Code Online (Sandbox Code Playgroud)

错误消息太冗长,如何向用户返回更好的错误?我想对 json 响应进行建模,如下所示:

{
    "errors": [
        "first_name": "This field is required",
        "last_name": "This field is required",
        "age": "This field is required",
        "email": "Invalid email"
    ]
}
Run Code Online (Sandbox Code Playgroud)

bla*_*een 18

Gin gonic 使用该包github.com/go-playground/validator/v10来执行绑定验证。如果验证失败,返回的错误是validator.ValidationErrors.

这没有明确提及,但在模型绑定和验证中它指出:

Gin 使用 go-playground/validator/v10 进行验证。在此处查看有关标签使用的完整文档。

该链接指向go-playground/validator/v10文档,您可以在其中找到段落Validation Functions Return Type error

您可以使用标准errors包来检查错误是否是这样,将其解包并访问单个字段,即validator.FieldError. 由此,您可以构建任何您想要的错误消息。

给定一个这样的错误模型:

type ApiError struct {
    Field string
    Msg   string
}
Run Code Online (Sandbox Code Playgroud)

你可以这样做:

    var u User
    err := c.BindQuery(&u);
    if err != nil {
        var ve validator.ValidationErrors
        if errors.As(err, &ve) {
            out := make([]ApiError, len(ve))
            for i, fe := range ve {
                out[i] = ApiError{fe.Field(), msgForTag(fe.Tag())}
            }
            c.JSON(http.StatusBadRequest, gin.H{"errors": out})
        }
        return
    }
Run Code Online (Sandbox Code Playgroud)

使用辅助函数为您的验证规则输出自定义错误消息:

func msgForTag(tag string) string {
    switch tag {
    case "required":
        return "This field is required"
    case "email":
        return "Invalid email"
    }
    return ""
}
Run Code Online (Sandbox Code Playgroud)

在我的测试中,输出:

{
    "errors": [
        {
            "Field": "Number",
            "Msg": "This field is required"
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

PS:要获得带有动态键的 json 输出,您可以使用map[string]string而不是固定的结构模型。