如果Gin验证失败,如何返回自定义错误

时间:2018-05-25 06:50:23

标签: go gin go-gin

例如,我有以下struct

type Address struct {
    City string `json:"city" binding:"required"`
    AddressLine string `json:"address_line" binding:"required"`
}

我有以下功能来处理来自用户的请求

func AddressCreate(c *gin.Context) {
    var address Address
    if err := c.BindJSON(&address); err == nil {
        // if everything is good save to database
        // and return success message
        db.Create(&address)
        c.JSON(http.StatusOK, gin.H {"status":"success"})
    } else {
        c.JSON(http.StatusBadRequest, err)
    }
}

预期的行为是以这种方式返回json

[
     {
         "city":"required"
     }
     {
         "address_line":"required"
     }
]

但我收到的错误格式如下

"Address.City": {
    "FieldNamespace": "Address.City",
    "NameNamespace": "City",
    "Field": "City",
    "Name": "City",
    "Tag": "required",
    "ActualTag": "required",
    "Kind": 24,
    "Type": {},
    "Param": "",
    "Value": ""
},
"Address.AddressLine": {
    "FieldNamespace": "AddressLine",
    "NameNamespace": "AddressLine",
    "Field": "AddressLine",
    "Name": "AddressLine",
    "Tag": "required",
    "ActualTag": "required",
    "Kind": 24,
    "Type": {},
    "Param": "",
    "Value": ""
}

我尝试了什么:
我创建了将error转换为ValidationErrors并迭代其中所有FieldError的函数

func ListOfErrors(e error) []map[string]string {
    ve := e.(validator.ValidationErrors)
    InvalidFields := make([]map[string]string, 0)

    for _, e := range ve {
        errors := map[string]string{}
        // field := reflect.TypeOf(e.NameNamespace)
        errors[e.Name] = e.Tag
        InvalidFields = append(InvalidFields, errors)
    }

    return InvalidFields
}

输出看起来好多了

[
    {
        "City":"required"
    },
    {
        "AddressLine":"required"
    }
]

但我无法用字段的名称解决问题。我无法将Name交换为我在结构标记name中注明的json:"city"。所以我的问题是,如果答案是肯定的,那么我选择正确的方法来解决问题如何获取字段的结构json标签?

2 个答案:

答案 0 :(得分:0)

您可以使用ToSnake来篡改名称:

import (
    "unicode"
)

// ToSnake convert the given string to snake case following the Golang format:
// acronyms are converted to lower-case and preceded by an underscore.
func ToSnake(in string) string {
    runes := []rune(in)
    length := len(runes)

    var out []rune
    for i := 0; i < length; i++ {
        if i > 0 && unicode.IsUpper(runes[i]) && ((i+1 < length && unicode.IsLower(runes[i+1])) || unicode.IsLower(runes[i-1])) {
            out = append(out, '_')
        }
        out = append(out, unicode.ToLower(runes[i]))
    }

    return string(out)
}



func ListOfErrors(e error) []map[string]string {
    ve := e.(validator.ValidationErrors)
    invalidFields := make([]map[string]string, 0)

    for _, e := range ve {
        errors := map[string]string{}
        errors[ToSnake(e.Name)] = e.Tag
        invalidFields = append(InvalidFields, errors)
    }

    return invalidFields
}

答案 1 :(得分:0)

如果您希望它与json标记中定义的相同,那么您应该使用反射从数据类型中提取该标记。

我没有您的图书馆,因此无法编辑和检查它。但我相信你所追求的应该是这样的:

func ListOfErrors(address *Address, e error) []map[string]string {
    ve := e.(validator.ValidationErrors)
    InvalidFields := make([]map[string]string, 0)

    for _, e := range ve {
        errors := map[string]string{}
        // field := reflect.TypeOf(e.NameNamespace)
        field, _ := reflect.TypeOf(address).Elem().FieldByName(e.Name)
        jsonTag := string(field.Tag.Get("json"))
        errors[jsonTag] = e.Tag
        InvalidFields = append(InvalidFields, errors)
    }

    return InvalidFields
}

请注意,由于address参数的类型基本上已知,因此有点人为。因此,并非严格要求作为函数参数。但您可以将address *Address更改为address interface{},并将其用于其他类型。

免责声明:为简洁起见,我跳过错误检查,但您当然应该检查代码中的错误(例如,没有此类字段错误或该字段上没有json标记)。