Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ spec, _ := openapi3.NewLoader().LoadFromData([]byte(rawSpec))
spec.Servers = nil

e := echo.New()
e.POST("/resource", func(c echo.Context) error {
e.POST("/resource", func(c *echo.Context) error {
fmt.Printf("%s /resource was called\n", c.Request().Method)

return c.NoContent(http.StatusNoContent)
Expand All @@ -56,8 +56,8 @@ e.Use(mw)

| Version | Supported? |
| -- | -- |
| `github.com/labstack/echo/v4` | |
| `github.com/labstack/echo/v5` | ❌ [Issue](https://github.com/oapi-codegen/echo-middleware/issues/37) |
| `github.com/labstack/echo/v4` | |
| `github.com/labstack/echo/v5` | |


### "This doesn't support ..." / "I think it's a bug that ..."
Expand Down
18 changes: 4 additions & 14 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,35 +1,25 @@
module github.com/oapi-codegen/echo-middleware

go 1.20
go 1.25.0

require (
github.com/getkin/kin-openapi v0.124.0
github.com/labstack/echo/v4 v4.12.0
github.com/stretchr/testify v1.9.0
github.com/labstack/echo/v5 v5.1.1
github.com/stretchr/testify v1.11.1
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-openapi/jsonpointer v0.20.2 // indirect
github.com/go-openapi/swag v0.22.8 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/invopop/yaml v0.2.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/labstack/gommon v0.4.2 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/time v0.14.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
44 changes: 15 additions & 29 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -7,56 +7,42 @@ github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1Rf
github.com/go-openapi/swag v0.22.8 h1:/9RjDSQ0vbFR+NyjGMkFTsA1IA0fmhKSThmfGZjicbw=
github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY=
github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0=
github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM=
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labstack/echo/v5 v5.1.1 h1:4QkvKoS8ps5ch49t8b72QS9Z581ytgxhTzxuB/CBA2I=
github.com/labstack/echo/v5 v5.1.1/go.mod h1:SyvlSdObGjRXeQfCCXW/sybkZdOOQZBmpKF0bvALaeo=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
65 changes: 32 additions & 33 deletions oapi_validate.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Provide HTTP middleware functionality to validate that incoming requests conform to a given OpenAPI 3.x specification.
//
// This provides middleware for an echo/v4 HTTP server.
// This provides middleware for an echo/v5 HTTP server.
//
// This package is a lightweight wrapper over https://pkg.go.dev/github.com/getkin/kin-openapi/openapi3filter from https://pkg.go.dev/github.com/getkin/kin-openapi.
//
Expand All @@ -21,8 +21,8 @@ import (
"github.com/getkin/kin-openapi/openapi3filter"
"github.com/getkin/kin-openapi/routers"
"github.com/getkin/kin-openapi/routers/gorillamux"
"github.com/labstack/echo/v4"
echomiddleware "github.com/labstack/echo/v4/middleware"
"github.com/labstack/echo/v5"
echomiddleware "github.com/labstack/echo/v5/middleware"
)

const (
Expand Down Expand Up @@ -53,7 +53,7 @@ func OapiRequestValidator(spec *openapi3.T) echo.MiddlewareFunc {
}

// ErrorHandler is called when there is an error in validation
type ErrorHandler func(c echo.Context, err *echo.HTTPError) error
type ErrorHandler func(c *echo.Context, err *echo.HTTPError) error

// MultiErrorHandler is called when the OpenAPI filter returns an openapi3.MultiError (https://pkg.go.dev/github.com/getkin/kin-openapi/openapi3#MultiError)
type MultiErrorHandler func(openapi3.MultiError) *echo.HTTPError
Expand Down Expand Up @@ -109,7 +109,7 @@ func OapiRequestValidatorWithOptions(spec *openapi3.T, options *Options) echo.Mi

skipper := getSkipperFromOptions(options)
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
return func(c *echo.Context) error {
if skipper(c) {
return next(c)
}
Expand All @@ -128,7 +128,7 @@ func OapiRequestValidatorWithOptions(spec *openapi3.T, options *Options) echo.Mi

// ValidateRequestFromContext is called from the middleware above and actually does the work
// of validating a request.
func ValidateRequestFromContext(ctx echo.Context, router routers.Router, options *Options) *echo.HTTPError {
func ValidateRequestFromContext(ctx *echo.Context, router routers.Router, options *Options) *echo.HTTPError {
req := ctx.Request()

if options != nil && options.Prefix != "" {
Expand All @@ -143,7 +143,7 @@ func ValidateRequestFromContext(ctx echo.Context, router routers.Router, options
// We failed to find a matching route for the request.
if err != nil {
if errors.Is(err, routers.ErrMethodNotAllowed) {
return echo.NewHTTPError(http.StatusMethodNotAllowed)
return echo.NewHTTPError(http.StatusMethodNotAllowed, http.StatusText(http.StatusMethodNotAllowed))
}

switch e := err.(type) {
Expand All @@ -154,8 +154,8 @@ func ValidateRequestFromContext(ctx echo.Context, router routers.Router, options
default:
// This should never happen today, but if our upstream code changes,
// we don't want to crash the server, so handle the unexpected error.
return echo.NewHTTPError(http.StatusInternalServerError,
fmt.Sprintf("error validating route: %s", err.Error()))
return newHTTPError(http.StatusInternalServerError,
fmt.Sprintf("error validating route: %s", err.Error()), err)
}
}

Expand Down Expand Up @@ -198,44 +198,35 @@ func ValidateRequestFromContext(ctx echo.Context, router routers.Router, options
// Split up the verbose error by lines and return the first one
// openapi errors seem to be multi-line with a decent message on the first
errorLines := strings.Split(e.Error(), "\n")
return &echo.HTTPError{
Code: http.StatusBadRequest,
Message: errorLines[0],
Internal: err,
}
return newHTTPError(http.StatusBadRequest, errorLines[0], err)
case *openapi3filter.SecurityRequirementsError:
for _, err := range e.Errors {
httpErr, ok := err.(*echo.HTTPError)
if ok {
var httpErr *echo.HTTPError
if errors.As(err, &httpErr) {
return httpErr
}
if status := echo.StatusCode(err); status != 0 {
return echo.NewHTTPError(status, err.Error())
}
}
return &echo.HTTPError{
Code: http.StatusForbidden,
Message: e.Error(),
Internal: err,
}
return newHTTPError(http.StatusForbidden, e.Error(), err)
default:
// This should never happen today, but if our upstream code changes,
// we don't want to crash the server, so handle the unexpected error.
return &echo.HTTPError{
Code: http.StatusInternalServerError,
Message: fmt.Sprintf("error validating request: %s", err),
Internal: err,
}
return newHTTPError(http.StatusInternalServerError, fmt.Sprintf("error validating request: %s", err), err)
}
}
return nil
}

// GetEchoContext gets the echo context from within requests. It returns
// nil if not found or wrong type.
func GetEchoContext(c context.Context) echo.Context {
func GetEchoContext(c context.Context) *echo.Context {
iface := c.Value(EchoContextKey)
if iface == nil {
return nil
}
eCtx, ok := iface.(echo.Context)
eCtx, ok := iface.(*echo.Context)
if !ok {
return nil
}
Expand All @@ -246,6 +237,18 @@ func GetUserData(c context.Context) any {
return c.Value(UserDataKey)
}

func newHTTPError(code int, message string, err error) *echo.HTTPError {
httpErr := echo.NewHTTPError(code, message)
if err == nil {
return httpErr
}
wrapped, ok := httpErr.Wrap(err).(*echo.HTTPError)
if !ok {
return httpErr
}
return wrapped
}

// attempt to get the skipper from the options whether it is set or not
func getSkipperFromOptions(options *Options) echomiddleware.Skipper {
if options == nil {
Expand Down Expand Up @@ -277,9 +280,5 @@ func getMultiErrorHandlerFromOptions(options *Options) MultiErrorHandler {
// of all of the errors. This method is called if there are no other
// methods defined on the options.
func defaultMultiErrorHandler(me openapi3.MultiError) *echo.HTTPError {
return &echo.HTTPError{
Code: http.StatusBadRequest,
Message: me.Error(),
Internal: me,
}
return newHTTPError(http.StatusBadRequest, me.Error(), me)
}
10 changes: 5 additions & 5 deletions oapi_validate_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (

"github.com/getkin/kin-openapi/openapi3"
"github.com/getkin/kin-openapi/openapi3filter"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v5"
middleware "github.com/oapi-codegen/echo-middleware"
)

Expand Down Expand Up @@ -79,13 +79,13 @@ components:
spec.Servers = nil

e := echo.New()
e.POST("/resource", func(c echo.Context) error {
e.POST("/resource", func(c *echo.Context) error {
fmt.Printf("%s /resource was called\n", c.Request().Method)

return c.NoContent(http.StatusNoContent)
})

e.GET("/protected_resource", func(c echo.Context) error {
e.GET("/protected_resource", func(c *echo.Context) error {
// NOTE that we're setting up our `authenticationFunc` (below) to /never/ allow any requests in - so if we get a response from this endpoint, our `authenticationFunc` hasn't correctly worked
return c.NoContent(http.StatusNoContent)
})
Expand Down Expand Up @@ -261,7 +261,7 @@ paths:
spec.Servers = nil

e := echo.New()
e.POST("/resource", func(c echo.Context) error {
e.POST("/resource", func(c *echo.Context) error {
fmt.Printf("%s /resource was called\n", c.Request().Method)

return c.NoContent(http.StatusNoContent)
Expand All @@ -272,7 +272,7 @@ paths:
return fmt.Errorf("this check always fails - don't let anyone in!")
}

errorHandlerFunc := func(c echo.Context, err *echo.HTTPError) error {
errorHandlerFunc := func(c *echo.Context, err *echo.HTTPError) error {
fmt.Printf("ErrorHandler: An HTTP %d was returned by the middleware with error message: %s\n", err.Code, err.Message)
return c.String(err.Code, "This was rewritten by the ErrorHandler")
}
Expand Down
Loading