go-playground / validator

:100:Go Struct and Field validation, including Cross Field, Cross Struct, Map, Slice and Array diving
MIT License
16.14k stars 1.29k forks source link

Access to StructField object in FieldError #1180

Open mbretter opened 9 months ago

mbretter commented 9 months ago

Package version eg. v9, v10:

v10

Issue, Question or Enhancement:

It would be great having direct access to the struct field object inside FieldError, at the moment the StructField() func just returns the name of the field. Something like RawStructField() StructField would help a lot when retrieving additional infos from the struct field like additional tags when doing the error handling.

Code sample, to showcase or reproduce:


type Address struct {
    Street string `json:"street" validate:"required" error:"required:global.street.required"`
}

err := validate.Struct(loginData)
if err != nil {
for _, err := range err.(validator.ValidationErrors) {

              fld := err.RawStructField()
              errStr := fld.Tag.Get("error")
              // do custom error handling, messages, etc
        }
}
ardrabczyk commented 7 months ago

Just want to say that of course you can do it yourself:

package main

import (
        "log"
        "reflect"

        "github.com/go-playground/validator"
)

type MySettings struct {
        Url      string `validate:"required,url" err:"url it must be non empty and must be correct URL"`
        Username string `validate:"required" err:"username must be non empty"`
        Password string `validate:"required" err:"password must be non empty"`
}

func main() {
        settings := MySettings{}
        validate := validator.New()
        if err := validate.Struct(&settings); err != nil {
                for _, err := range err.(validator.ValidationErrors) {
                        fld := reflect.ValueOf(settings).Type()
                        a, _ := fld.FieldByName(err.Field())
                        log.Println("errorstr ==", a.Tag.Get("err"))
                }
        }
}

Output will be:

2023/12/18 09:05:34 errorstr == url it must be non empty and must be correct URL
2023/12/18 09:05:34 errorstr == username must be non empty
2023/12/18 09:05:34 errorstr == password must be non empty

And now there is also this https://github.com/go-playground/validator/pull/1183.

mbretter commented 7 months ago

Does this work for nested fields, if err.Field() is something like "Foo.Bar"?

mbretter commented 7 months ago

atm I have to seek through the whole struct, with something like:


p := strings.Split(err.StructNamespace(), ".") // struct field name path, e.g. Foo.Bar
p = p[1:]
fld := getStructField(t, p)
...
func getStructField(s reflect.Type, path []string) *reflect.StructField {
    var field reflect.StructField

    t := s
    for _, p := range path {
        if t.Kind() == reflect.Ptr {
            t = t.Elem()
        }

        f, ok := t.FieldByName(p)
        if ok {
            field = f
            t = f.Type
        }
    }

    return &field
}