gin-gonic / gin

Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance -- up to 40 times faster. If you need smashing performance, get yourself some Gin.
https://gin-gonic.com/
MIT License
79.22k stars 8.03k forks source link

Getting the correct field name on bind validation errors #4102

Open OranShuster opened 1 week ago

OranShuster commented 1 week ago

Description

When gettig an error back from ShouldBind* methods, im looking for a way to return to the user the correct field names Looking at the ValidationErrors and FieldError structs i don't see the actual name of the query param Is this a validator limitation or can this be solved by gin?

Side note - this part of the gin documentation is a bit lacking. it would be very beneficial to add an example about using ValidationErrors , instead of just a normal go error, when binding

How to reproduce

package main

import (
    "errors"
    "net/http"

    "github.com/gin-gonic/gin"
    "github.com/go-playground/validator/v10"
)

type queryTestStruct struct {
    Param1 string `form:"othername" binding:"required"`
}

func main() {
    r := gin.Default()
    r.GET("/testQuery", func(c *gin.Context) {
        var params queryTestStruct
        err := c.ShouldBindQuery(&params)
        if err != nil {
            var ve validator.ValidationErrors
            if errors.As(err, &ve) {
                out := map[string]interface{}{}
                for _, fe := range ve {
                    fieldName := fe.Field()
                    error := fe.Error()
                    out[fieldName] = error
                }
                c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errors": out})
            }
            return
        }
        c.JSON(200, "No error")
    })
    r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}

Expectations

I expect to have some way to get from the FieldError struct the actual expected query param name: othername

Actual result

$ curl --location 'localhost:8080/testQuery'
{
    "errors": {
        "Param1": "Key: 'queryTestStruct.Param1' Error:Field validation for 'Param1' failed on the 'required' tag"
    }
}

Environment

jerryyummy commented 1 week ago

you do not include the parameter, should use :curl -G "http://localhost:8080/testQuery" --data-urlencode "othername=your_value"

OranShuster commented 1 week ago

you do not include the parameter, should use :curl -G "http://localhost:8080/testQuery" --data-urlencode "othername=your_value"

you are misunderstanding my question, im talking about query parameters error handling. obviously if ill send the parameter i won't get any error

GenJi77JYXC commented 4 days ago

Maybe you can solve this problem using reflection

Here are some of my solutions

package main

import (
    "errors"
    "fmt"
    "github.com/go-playground/validator/v10"
    "net/http"
    "reflect"

    "github.com/gin-gonic/gin"
)

type queryTestStruct struct {
    Param1 string `form:"othername" binding:"required"`
}

func main() {
    r := gin.Default()
    r.GET("/testQuery", func(c *gin.Context) {
        var params queryTestStruct
        err := c.ShouldBindQuery(&params)
        if err != nil {
            rType := reflect.TypeOf(new(queryTestStruct)).Elem()

            var ve validator.ValidationErrors
            if errors.As(err, &ve) {
                out := map[string]interface{}{}
                for _, fe := range ve {
                    fieldName := fe.Field()
                    param, ok := rType.FieldByName(fieldName)
                    if ok {
                        fmt.Println(param.Tag.Get("form"))//  // Get param's correct field name
                    }
                    error := fe.Error()
                    out[fieldName] = error
                    out["tagName"] = param.Tag.Get("form")
                }
                c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errors": out})
            }
            return
        }
        c.JSON(200, "No error")
    })
    r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}

Actual result

curl : {"errors":{"Param1":"Key: 'queryTestStruct.Param1' Error:Field validation for 'Param1' failed on the 'required' tag","tagName":"othername"}}

I used the tagName field to return the information you want