govalidator icon indicating copy to clipboard operation
govalidator copied to clipboard

Option to report struct errors based on json field name

Open nickithewatt opened this issue 9 years ago • 14 comments

Hi,

Quite often structs are loaded from json using the standard json go functionality, i.e a struct definition below is quite common:

type User struct {
    FirstName string `json:"first_name" valid:"required"`
    LastName string `json:"first_name" valid:"required"`
}

Assuming we use the govalidator to validate our struct, it would be very handy to have the option to be able to specify that we would rather have error messages report fields names based on the json field name rather than the struct name per se.

i.e. instead of FirstName: non zero value required rather first_name: non zero value required

Is this possible?

nickithewatt avatar Dec 12 '15 15:12 nickithewatt

+1!!!!

kidtronnix avatar Jan 18 '16 23:01 kidtronnix

If the intent is make the messages more user friendly I think that would be better use a field for these only purpose.

waltton avatar Jan 18 '16 23:01 waltton

Hmmm i see your point and in some ways it is more flexible. But also the the json tag is what the user will practically see for any restful JSON applications, which I believe will be a alot of use cases.

Could we potentially have some way of specifying a custom tag field?

kidtronnix avatar Jan 19 '16 01:01 kidtronnix

We wanted the same thing, we ended up using reflect to get the validation subject struct fields, extract their json tags to get the field name we want to return in responses, something like this:

func ValidationError(ctx echo.Context, s interface{}, err error) error {
    switch err.(type) {
    case govalidator.Errors:
        // Use reflect to get the raw struct element
        typ := reflect.TypeOf(s).Elem()
        if typ.Kind() != reflect.Struct {
            return UnexpectedError(ctx, errors.New("validation subject is not a struct"))
        }

        // This is will contain the errors we return back to user
        errs := map[string]string{}
        // Errors found by the validator
        errsByField := govalidator.ErrorsByField(err.(govalidator.Errors))
        // Loop over our struct fields
        for i := 0; i < typ.NumField(); i++ {
            // Get the field
            f := typ.Field(i)
            // Do we have an error for the field
            e, ok := errsByField[f.Name]
            if ok {
                // Try and get the `json` struct tag
                name := strings.Split(f.Tag.Get("json"), ",")[0]
                // If the name is - we should ignore the field
                if name == "-" {
                    continue
                }
                // If the name is not blank we add it our error map
                if name != "" {
                    errs[name] = e
                    continue
                }
                // Finall if all else has failed just add the raw field name to the
                // error map
                errs[f.Name] = e
            }
        }

        // Return the validation error
        mapper := &Errors{Message: "Validation Error", Errors: errs}
        return ErrorHandler(422, mapper, ctx)
    }

    // If the error type is not a govalidator.Errors, return a 500
    return UnexpectedError(
        ctx,
        fmt.Errorf("Expected govalidator.Errors, got %s", reflect.TypeOf(err)))
}

krak3n avatar Jul 28 '16 14:07 krak3n

@krak3n could you put your example into pull request with sample of usage and small note in docs please?

asaskevich avatar Aug 27 '16 10:08 asaskevich

@asaskevich sure, I'll try and do it over the weekend 😃

krak3n avatar Aug 27 '16 11:08 krak3n

Looking forward to this PR.

Is it just going to use the json:"value" as it's field name, or perhaps different approach will be taken?

Thanks.

paroxp avatar Sep 02 '16 09:09 paroxp

@paroxp I didn't get to it last weekend but hopefully this weekend, it's just going to be documentation how how you could use reflection to get the json, xml or what ever other struct tag to get the field name you want to use for the error value.

krak3n avatar Sep 02 '16 10:09 krak3n

do we have updates or solution?

IAD avatar Nov 10 '16 09:11 IAD

What about nested field names?

If we validate the following, we get Name: invalid_alphanum does not validate as alphanum. However, we cannot identify which failed when nested fields contain the same name.

type Resource struct {
    Name string `json:"name" valid:"alphanum"`
    Version string `json:"version" valid:"required"`
    Env struct {
        Name string `json:"name" valid:"alphanum"`
        Value string `json:"value" valid:"required"`
    }
}

retr0h avatar Sep 22 '17 02:09 retr0h

@retr0h it should return something like Env.Name: invalid_alphanum does not validate as alphanum or Env.name ...?

asaskevich avatar Nov 03 '17 20:11 asaskevich

^ Yes, that would be the expected behavior, however is not the case currently

saifabid avatar Mar 28 '18 02:03 saifabid

@asaskevich *

saifabid avatar Mar 28 '18 02:03 saifabid

Hello guys! I forked this package cause owner disappeared. Hope, he will be back, but it would be easier to merge these changes back if he is back Link to my repo: create issue there and we'll discuss it.

sergeyglazyrindev avatar Oct 17 '21 21:10 sergeyglazyrindev