oapi-codegen icon indicating copy to clipboard operation
oapi-codegen copied to clipboard

Field validation

Open quen2404 opened this issue 5 years ago • 2 comments

Hi, thank you for your project, it's exactly what i'm looking for some time :100: I'm also developing rest API using Go and Echo and we use OpenAPI to document ours apis. In my apis, i'm a huge fan & user of package go-playground/validator to validate data i received (like min string length , etc...). It will be awesome if you could support this in schema structure generation. What do you think ? Do you need help to do that ?

quen2404 avatar Sep 07 '20 15:09 quen2404

One thing that could be great would be to use the JSON-Schema tags that can be included in the openapi schema itself https://swagger.io/docs/specification/data-models/keywords/

Some libs already exist to use JSON Schemas like https://github.com/xeipuuv/gojsonschema so the work may not be too huge

skateinmars avatar Sep 29 '20 09:09 skateinmars

I had a similar requirement and I was able to build a viable solution by generating an echo server using oapi-codegen, and then registering a custom validator that implements go-playground/validator as mentioned in the echo documentation.

This is how you could do it -

Add a custom validate tag in the swagger for the fields that require validation say

 x-oapi-codegen-extra-tags:
            validate: excluded_with_all

The models get generated with this custom tag

`json:"id,omitempty" validate:"excluded_with_all"`

kolluria avatar Dec 08 '22 14:12 kolluria

@kolluria did you have to call echo_ctx.Validate(myStructPointer) manually for each handler?

bofm avatar Feb 15 '23 09:02 bofm

@bofm Yeah. You have to manually invoke echo_ctx.Validate after binding the request body to the struct.

For instance,

func (s serverImplementation) SomeHandler(c echo.Context) error {
	var body SomeJSONRequestBody
	err := c.Bind(&body)
	if err != nil {
		return err
	}

	err = c.Validate(&body)
	if err != nil {
		return err
	}
       // Your business logic goes here
}

kolluria avatar Feb 15 '23 10:02 kolluria

@kolluria thanks for the answer.

I just found a way to not have to (and not forget to) call Validate() in each handler with the strict server:

type StructValidator struct {
    validator *validator.Validate
}

func (v StructValidator) Validate(i interface{}) error {
    if err := v.validator.Struct(i); err != nil {
        return err
    }
    return nil
}

func validateStructMiddeware(f api.StrictHandlerFunc, operationID string) api.StrictHandlerFunc {
    return func(ctx echo.Context, i interface{}) (interface{}, error) {
        if err := ctx.Validate(i); err != nil {
            return nil, fmt.Errorf("%s failed to validate request body: %w", operationID, err)
        }
        return f(ctx, i)
    }
}

func main() {
    e := echo.New()
    e.Validator = StructValidator{validator: v}

    apiServer := api.NewStrictHandler(
        api.NewServer(theStorage),
        []api.StrictMiddlewareFunc{
            validateStructMiddeware,
        },
    )
    api.RegisterHandlers(e, apiServer)
}

bofm avatar Feb 15 '23 13:02 bofm

this does not seem to work for nested fields. has anyone had success with tagging a nested field edit: using the dive tag solved the problem

pbikki avatar Apr 11 '23 16:04 pbikki