go-ozzo / ozzo-validation

An idiomatic Go (golang) validation package. Supports configurable and extensible validation rules (validators) using normal language constructs instead of error-prone struct tags.
MIT License
3.73k stars 224 forks source link

Details on errors #141

Closed maeglindeveloper closed 3 years ago

maeglindeveloper commented 3 years ago

Hi everyone,

I'm actually wondering if there is any way with go-ozzo to have some details on errors but in a more structured way ? Let me explain:

Let say I have the following code and an input like this with some validation

type MyInput struct {
    Email string
}

func (i MyInput) Validate() error {
    return validation.ValidateStruct(&i,
        validation.Field(&i.Email, is.Email, validation.Length(30, 100)),
    )
}

If for instance the Length rule failed, I will have an object of type validation.Errors which is basically a map[string]error. Of course it is fine, but is there any way to have something more structured ?

For instance could we have / imagine something map[string]ValidationError with the definition of ValidationError like this ?

type ValidationError struct {
    Code       string
    Message    string
    Parameters map[string]interface{}
}

Which can be easier to use / analyse that a simple string ?

As an example:

What we have now (json way)

{
    "Email":"the length must be between 30 and 100. "
}

What I would like :) (json way)

{
    "Email": {
        "Message":"the length must be between 30 and 100. ",
        "Code": "LenghtError",
        "Parameters": {
            "min": 30,
            "max": 100,
            "current": 4,
        }
    }
}

This is a total opened topic of course, all advices are really welcomed :+1:

uroshercog commented 3 years ago

What you are proposing is already possible now, but you have to remap the errors in the map. So what I did is iterate over the map and type assert the error to validation.Error interface which includes Code(), Params() and Message() functions (and a couple more). Then construct the error any way you see fit. I'm not sure all built-in validators return errors if this type so I handle them differently.

Below is an example of how I structured my responses. The code and params for email in test is returned by the built-in validation.Length validator. You can still extend the built-in validators and include additional params, e.g. current. Hope this helps.

{
  "code": "validation_failed",
  "details": {
    "email": {
      "code": "validation_length_out_of_range",
      "params": {
        "max": 100,
        "min": 30
      }
    }
  }
}

But I agree it would be best to have a way to set a custom serializer so we don't have to mangle the errors ourselves.

maeglindeveloper commented 3 years ago

hey @uroshercog that sounds like a first good step to me :) ! Thanks for the help. I did a wrapperErrors function that did the job. I did not realized that I was not using the v4 ... :-1: Now I see the validation.Error interface, which obviously helps to handle that wrapper :+1:

Thanks a lot !!