When there is a field type error in the JSON sent from the frontend, an error occurs stating that the JSON parsing has failed. I have a custom validator, but it only validates the field content when the JSON parsing is successful. In the case of JSON parsing failure, I cannot obtain errors of type validator.ValidationErrors; I can only get json.UnmarshalTypeError. Is there any way to customize the content of this JSON parsing error?
"json: cannot unmarshal number into Go struct field argsLogin.Username of type string"
my controller code:
func (l *Login) Login(c *gin.Context) {
args := argsLogin{}
if err := c.ShouldBind(&args); err != nil {
bean.Response.ResultFail(c, 10001, common.GetVerErr(err))
return
}
login, err := manage.LoginLogic.Login(args.Username, args.Password, common.GetDeviceType(c), constant.SystemTerminalAdmin)
if err != nil {
bean.Response.ResultFail(c, 10002, err.Error())
return
}
bean.Response.ResultSuc(c, "登录成功", &login)
}
my validator:
func GetVerErr(err error) string {
er, ok := err.(validator.ValidationErrors)
if ok {
field := er[0].Field()
if field == er[0].StructField() {
field = strings.ToLower(field[0:1]) + field[1:]
}
switch er[0].Tag() {
case "required":
return field + "不能为空"
case "min":
if er[0].Type().String() == "string" {
return field + "不能小于" + er[0].Param() + "位"
}
return field + "不能小于" + er[0].Param()
case "gte":
if er[0].Type().String() == "string" {
return field + "不能小于" + er[0].Param() + "位"
}
return field + "不能小于" + er[0].Param()
}
return field + "错误"
} else {
return "参数格式错误"
}
}
I can replace the entire string of the error message, but I want to take it a step further and indicate which field failed to parse. How can I obtain the field that caused the parsing failure?
Comment From: Ozoniuss
For simplicity, let's assume argsLogin
looked like
type argsLogin struct {
Name string `validate:"required" json:"name"`
Password string `validate:"required,min=8" json:"password"`
LoginCode int `validate:"gte=1000,lte=9999" json:"logincode"`
}
And in this case
{
"name": "adrian",
"password": "12345678",
"logincode": "1111" // should be 1111
}
you want to specify that logincode
field was not parsed correctly right?
Because ShouldBind
uses the json Decoder from encoding/json package under the hood in this case, you indeed get an error during the call to decoder.Decode
(see the gin implementation). In the case the error is a *json.UnmarshalTypeError
like you mentioned, you can do something like this:
func main() {
var postForm string = `
{
"name": "adrian",
"password": "12345678",
"logincode": "1111"
}
`
var args argsLogin
r := strings.NewReader(postForm)
dec := json.NewDecoder(r)
err := dec.Decode(&args)
if err != nil {
switch err.(type) {
case *json.UnmarshalTypeError:
jsonerr := err.(*json.UnmarshalTypeError)
fmt.Println("field with error:", jsonerr.Field)
break
default:
fmt.Println("default error:", err)
break
}
}
return
}
$ go run main.go
field with error: logincode
Check out the json.UnmarshalTypeError
struct for more details regarding what lies under the implementation of that error.
However, keep in mind that it's possible the JSON parsing fails for whatever other reason, not just a field parsing, like if you were to input
{
"name": "adrian",
"password": "12345678",mistake
"logincode": "1111"
}
you would get a *json.SyntaxError
error
$ go run main.go
default error: invalid character 'm' looking for beginning of object key string
Not sure what you want to do in that case, but asserting for the *json.UnmarshalTypeError
should be a good starting point.
Possible example of how your controller could be updated:
func (l *Login) Login(c *gin.Context) {
args := argsLogin{}
if err := c.ShouldBind(&args); err != nil {
parsefielderr, ok := err.(*json.UnmarshalTypeError)
if !ok {
bean.Response.ResultFail(c, 10001, "whatever custom message with parsefielderr")
return
} else {
// whatever logic for other errors such as the one mentioned above
}
return
}
login, err := manage.LoginLogic.Login(args.Username, args.Password, common.GetDeviceType(c), constant.SystemTerminalAdmin)
if err != nil {
bean.Response.ResultFail(c, 10002, err.Error())
return
}
bean.Response.ResultSuc(c, "登录成功", &login)
}
Comment From: meng-fucius
@Ozoniuss Thank you very much for your response! This is extremely helpful for me!