Skip to content

RouteNotFound handler does not falls back to root one #2485

Description

@endorama

Issue Description

I think I found a bug in how RouteNotFound handler is propagated to route groups.

When using groups with a dedicated middleware there is no fallback to the root RouteNotFound handler, while that happens when no middleware is specified.

Checklist

  • Dependencies installed (not sure what this means)
  • No typos
  • Searched existing issues and docs

Expected behaviour

The root RouteNotFound handler is used.

Actual behaviour

An internal(?) handler is used

Steps to reproduce

Put the code below in a test file, run go test ./...

Working code to debug

This is a test file that reproduce the issue.

package main_test

import (
	"io"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/labstack/echo/v4"
	"github.com/stretchr/testify/require"
)

func server() http.Handler {
	e := echo.New()
	e.HideBanner = true
	e.HidePort = true

	// expect this handler is used as fallback unless a more specific is present
	e.RouteNotFound("/*", missingRouteHandler)

	// addig a middleware to the group overrides the RouteNotFound handler
	v0 := e.Group("/v0", func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) error {
			return next(c)
		}
	})
	// adding this would make it work
	// v0.RouteNotFound("/*", missingRouteHandler)
	v0.POST("/resource", handler)

	// the same does not happen when no middleware is set
	v1 := e.Group("/v1")
	v1.POST("/resource", handler)

	return e
}

func missingRouteHandler(c echo.Context) error {
	return c.NoContent(http.StatusNotFound)
}

func handler(c echo.Context) error {
	return nil
}

func TestMissing(t *testing.T) {
	srv := httptest.NewServer(server())

	routes := []string{
		// this is successful
		"/foo",
		// this fails
        //   Error Trace:    /home/endorama/code/REDACTED/main_test.go:68
        //   Error:          "{"message":"Not Found"}
        //                   " should have 0 item(s), but has 24
        //   Test:           TestMissing//v0/foo
		"/v0/foo",
		// this is successful
		"/v1/foo",
	}

	for _, path := range routes {
		t.Run(path, func(t *testing.T) {
			req, _ := http.NewRequest("POST", srv.URL+path, nil)

			resp, err := srv.Client().Do(req)
			require.NoError(t, err)
			defer resp.Body.Close()

			b, err := io.ReadAll(resp.Body)
			require.NoError(t, err)

			require.Equal(t, http.StatusNotFound, resp.StatusCode)
			require.Len(t, b, 0)
			require.Equal(t, "0", resp.Header.Get("Content-Length"))
		})
	}
}

Version/commit

github.com/labstack/echo/v4 v4.11.1

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions