Skip to content
Merged
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
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ This is _intended_ to be used with code that's generated through [`oapi-codegen`
> Using Echo v4? You want [`oapi-codegen/echo-middleware`](https://github.com/oapi-codegen/echo-middleware) instead.

> [!WARNING]
> The conversion of this repository from Echo v4 to Echo v5 is in progress, and no release has been tagged yet. This notice will be removed with the first release.

⚠️ This README may be for the latest development version, which may contain unreleased changes. Please ensure you're looking at the README for the latest release version.

Expand Down
13 changes: 2 additions & 11 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
module github.com/oapi-codegen/echo-v5-middleware

go 1.24.4
go 1.25.0

require (
github.com/getkin/kin-openapi v0.134.0
github.com/labstack/echo/v4 v4.15.1
github.com/labstack/echo/v5 v5.2.1
github.com/stretchr/testify v1.11.1
)

Expand All @@ -14,23 +14,14 @@ require (
github.com/go-openapi/swag v0.23.0 // indirect
github.com/gorilla/mux v1.8.1 // 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.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/oasdiff/yaml v0.0.0-20260313112342-a3ea61cb4d4c // indirect
github.com/oasdiff/yaml3 v0.0.0-20260224194419-61cd415a242b // 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
github.com/woodsbury/decimal128 v1.3.0 // indirect
golang.org/x/crypto v0.46.0 // indirect
golang.org/x/net v0.48.0 // indirect
golang.org/x/sys v0.39.0 // indirect
golang.org/x/text v0.32.0 // indirect
golang.org/x/time v0.14.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
27 changes: 6 additions & 21 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,10 @@ 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/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labstack/echo/v4 v4.15.1 h1:S9keusg26gZpjMmPqB5hOEvNKnmd1lNmcHrbbH2lnFs=
github.com/labstack/echo/v4 v4.15.1/go.mod h1:xmw1clThob0BSVRX1CRQkGQ/vjwcpOMjQZSZa9fKA/c=
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/labstack/echo/v5 v5.2.1 h1:TzpIksY6zLMzV0T0ycYbvTEoj9w6o6AcL5twg182VTY=
github.com/labstack/echo/v5 v5.2.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.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
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/oasdiff/yaml v0.0.0-20260313112342-a3ea61cb4d4c h1:7ACFcSaQsrWtrH4WHHfUqE1C+f8r2uv8KGaW0jTNjus=
Expand All @@ -42,21 +36,12 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
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=
github.com/woodsbury/decimal128 v1.3.0 h1:8pffMNWIlC0O5vbyHWFZAt5yWvWcrHA+3ovIIjVWss0=
github.com/woodsbury/decimal128 v1.3.0/go.mod h1:C5UTmyTjW3JftjUFzOVhC20BEQa2a4ZKOB5I6Zjb+ds=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
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=
Expand Down
46 changes: 23 additions & 23 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, "")
}

switch e := err.(type) {
Expand Down Expand Up @@ -199,29 +199,30 @@ func ValidateRequestFromContext(ctx echo.Context, router routers.Router, options
// 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,
Code: http.StatusBadRequest,
Message: errorLines[0],
}
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
}
var coder interface{ StatusCode() int }
if errors.As(err, &coder) {
return echo.NewHTTPError(coder.StatusCode(), err.Error())
}
}
return &echo.HTTPError{
Code: http.StatusForbidden,
Message: e.Error(),
Internal: err,
Code: http.StatusForbidden,
Message: e.Error(),
}
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,
Code: http.StatusInternalServerError,
Message: fmt.Sprintf("error validating request: %s", err),
}
}
}
Expand All @@ -230,12 +231,12 @@ func ValidateRequestFromContext(ctx echo.Context, router routers.Router, options

// 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 Down Expand Up @@ -278,8 +279,7 @@ func getMultiErrorHandlerFromOptions(options *Options) MultiErrorHandler {
// methods defined on the options.
func defaultMultiErrorHandler(me openapi3.MultiError) *echo.HTTPError {
return &echo.HTTPError{
Code: http.StatusBadRequest,
Message: me.Error(),
Internal: me,
Code: http.StatusBadRequest,
Message: me.Error(),
}
}
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-v5-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
31 changes: 15 additions & 16 deletions oapi_validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import (

"github.com/getkin/kin-openapi/openapi3"
"github.com/getkin/kin-openapi/openapi3filter"
"github.com/labstack/echo/v4"
echomiddleware "github.com/labstack/echo/v4/middleware"
"github.com/labstack/echo/v5"
echomiddleware "github.com/labstack/echo/v5/middleware"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -79,7 +79,7 @@ func TestOapiRequestValidator(t *testing.T) {
// Set up an authenticator to check authenticated function. It will allow
// access to "someScope", but disallow others.
options := Options{
ErrorHandler: func(c echo.Context, err *echo.HTTPError) error {
ErrorHandler: func(c *echo.Context, err *echo.HTTPError) error {
return c.String(err.Code, "test: "+err.Error())
},
Options: openapi3filter.Options{
Expand Down Expand Up @@ -111,7 +111,7 @@ func TestOapiRequestValidator(t *testing.T) {

// Install a request handler for /resource. We want to make sure it doesn't
// get called.
e.GET("/resource", func(c echo.Context) error {
e.GET("/resource", func(c *echo.Context) error {
called = true
return nil
})
Expand Down Expand Up @@ -155,7 +155,7 @@ func TestOapiRequestValidator(t *testing.T) {
}

// Add a handler for the POST message
e.POST("/resource", func(c echo.Context) error {
e.POST("/resource", func(c *echo.Context) error {
called = true
return c.NoContent(http.StatusNoContent)
})
Expand Down Expand Up @@ -187,7 +187,7 @@ func TestOapiRequestValidator(t *testing.T) {
called = false
}

e.GET("/protected_resource", func(c echo.Context) error {
e.GET("/protected_resource", func(c *echo.Context) error {
called = true
return c.NoContent(http.StatusNoContent)

Expand All @@ -201,7 +201,7 @@ func TestOapiRequestValidator(t *testing.T) {
called = false
}

e.GET("/protected_resource2", func(c echo.Context) error {
e.GET("/protected_resource2", func(c *echo.Context) error {
called = true
return c.NoContent(http.StatusNoContent)
})
Expand All @@ -213,7 +213,7 @@ func TestOapiRequestValidator(t *testing.T) {
called = false
}

e.GET("/protected_resource_401", func(c echo.Context) error {
e.GET("/protected_resource_401", func(c *echo.Context) error {
called = true
return c.NoContent(http.StatusNoContent)
})
Expand Down Expand Up @@ -252,7 +252,7 @@ func TestOapiRequestValidatorWithOptionsMultiError(t *testing.T) {

// Install a request handler for /resource. We want to make sure it doesn't
// get called.
e.GET("/multiparamresource", func(c echo.Context) error {
e.GET("/multiparamresource", func(c *echo.Context) error {
called = true
return nil
})
Expand Down Expand Up @@ -346,9 +346,8 @@ func TestOapiRequestValidatorWithOptionsMultiErrorAndCustomHandler(t *testing.T)
},
MultiErrorHandler: func(me openapi3.MultiError) *echo.HTTPError {
return &echo.HTTPError{
Code: http.StatusTeapot,
Message: me.Error(),
Internal: me,
Code: http.StatusTeapot,
Message: me.Error(),
}
},
}
Expand All @@ -360,7 +359,7 @@ func TestOapiRequestValidatorWithOptionsMultiErrorAndCustomHandler(t *testing.T)

// Install a request handler for /resource. We want to make sure it doesn't
// get called.
e.GET("/multiparamresource", func(c echo.Context) error {
e.GET("/multiparamresource", func(c *echo.Context) error {
called = true
return nil
})
Expand Down Expand Up @@ -454,7 +453,7 @@ func TestOapiRequestValidatorWithPrefix(t *testing.T) {
called := false

// Register handler under the prefixed path (as echo sees it)
e.GET("/api/resource", func(c echo.Context) error {
e.GET("/api/resource", func(c *echo.Context) error {
called = true
// The original request path should be preserved for the handler
assert.Equal(t, "/api/resource", c.Request().URL.Path)
Expand Down Expand Up @@ -501,11 +500,11 @@ func TestEncodedPathParams(t *testing.T) {

// Register handlers for parameterized paths. These must be registered
// before any requests are made so echo allocates enough param slots.
e.GET("/resource/maxlength/:param", func(c echo.Context) error {
e.GET("/resource/maxlength/:param", func(c *echo.Context) error {
called = true
return c.NoContent(http.StatusNoContent)
})
e.GET("/resource/pattern/:param", func(c echo.Context) error {
e.GET("/resource/pattern/:param", func(c *echo.Context) error {
called = true
return c.NoContent(http.StatusNoContent)
})
Expand Down
Loading