英文:
Return custom error message from struct tag validation
问题
我正在使用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"`
}
在处理程序中,我可以这样获取错误:
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()})
}
错误消息将会是:
{
"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"
}
错误消息太冗长了,有没有办法返回更好的错误给用户呢?我想将JSON响应模型化为:
{
"errors": [
"first_name": "This field is required",
"last_name": "This field is required",
"age": "This field is required",
"email": "Invalid email"
]
}
英文:
I'm using Go 1.17 with Gin and I want to implement a struct validation before sending the data to my database. I took the example from Gin documentation.
In the struct we can declare different tags to validate a field like this:
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"`
}
And in the handler I can grab the error like this:
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()})
}
The error message will be:
{
"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"
}
The error messages are too verbose how it's possible to returns a better error to the user? I'd like to model the json response like:
{
"errors": [
"first_name": "This field is required",
"last_name": "This field is required",
"age": "This field is required",
"email": "Invalid email"
]
}
答案1
得分: 13
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
}
你可以这样做:
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
}
使用一个辅助函数来输出自定义的验证规则错误消息:
func msgForTag(tag string) string {
switch tag {
case "required":
return "This field is required"
case "email":
return "Invalid email"
}
return ""
}
在我的测试中,这将输出:
{
"errors": [
{
"Field": "Number",
"Msg": "This field is required"
}
]
}
注意:为了获得具有动态键的JSON输出,你可以使用map[string]string
而不是固定的结构模型。
英文:
Gin gonic uses the package github.com/go-playground/validator/v10
to perform binding validation. If the validation fails, the error returned is a validator.ValidationErrors
.
This is not mentioned explicitly but here in Model binding and validation it states:
> Gin uses go-playground/validator/v10 for validation. Check the full docs on tags usage here.
That links to the go-playground/validator/v10
documentation, where you find the paragraph Validation Functions Return Type error.
You can use the standard errors
package to check if the error is that, unwrap it, and access the single fields, which are validator.FieldError
. From that, you can construct whatever error message you want.
Given an error model like this:
type ApiError struct {
Field string
Msg string
}
You can do this:
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
}
with a helper function to output a custom error message for your validation rules:
func msgForTag(tag string) string {
switch tag {
case "required":
return "This field is required"
case "email":
return "Invalid email"
}
return ""
}
In my test, this outputs:
{
"errors": [
{
"Field": "Number",
"Msg": "This field is required"
}
]
}
PS: To have a json output with dynamic keys, you can use map[string]string
instead of a fixed struct model.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论