Skip to content

Commit

Permalink
fix handler order in routing
Browse files Browse the repository at this point in the history
  • Loading branch information
ReneWerner87 committed Feb 22, 2025
1 parent 283ef32 commit e5ef561
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 50 deletions.
12 changes: 6 additions & 6 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,7 @@ func (app *App) Use(args ...any) Router {
return app
}

app.register([]string{methodUse}, prefix, nil, nil, handlers...)
app.register([]string{methodUse}, prefix, nil, handlers...)
}

return app
Expand Down Expand Up @@ -810,15 +810,15 @@ func (app *App) Patch(path string, handler Handler, middleware ...Handler) Route
}

// Add allows you to specify multiple HTTP methods to register a route.
func (app *App) Add(methods []string, path string, handler Handler, middleware ...Handler) Router {
app.register(methods, path, nil, handler, middleware...)
func (app *App) Add(methods []string, path string, handler Handler, handlers ...Handler) Router {
app.register(methods, path, nil, append([]Handler{handler}, handlers...)...)

return app
}

// All will register the handler on all HTTP methods
func (app *App) All(path string, handler Handler, middleware ...Handler) Router {
return app.Add(app.config.RequestMethods, path, handler, middleware...)
func (app *App) All(path string, handler Handler, handlers ...Handler) Router {
return app.Add(app.config.RequestMethods, path, handler, handlers...)
}

// Group is used for Routes with common prefix to define a new sub-router with optional middleware.
Expand All @@ -828,7 +828,7 @@ func (app *App) All(path string, handler Handler, middleware ...Handler) Router
func (app *App) Group(prefix string, handlers ...Handler) Router {
grp := &Group{Prefix: prefix, app: app}
if len(handlers) > 0 {
app.register([]string{methodUse}, prefix, grp, nil, handlers...)
app.register([]string{methodUse}, prefix, grp, handlers...)
}
if err := app.hooks.executeOnGroupHooks(*grp); err != nil {
panic(err)
Expand Down
48 changes: 24 additions & 24 deletions group.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func (grp *Group) Use(args ...any) Router {
return grp
}

grp.app.register([]string{methodUse}, getGroupPath(grp.Prefix, prefix), grp, nil, handlers...)
grp.app.register([]string{methodUse}, getGroupPath(grp.Prefix, prefix), grp, handlers...)
}

if !grp.anyRouteDefined {
Expand All @@ -109,60 +109,60 @@ func (grp *Group) Use(args ...any) Router {

// Get registers a route for GET methods that requests a representation
// of the specified resource. Requests using GET should only retrieve data.
func (grp *Group) Get(path string, handler Handler, middleware ...Handler) Router {
return grp.Add([]string{MethodGet}, path, handler, middleware...)
func (grp *Group) Get(path string, handler Handler, handlers ...Handler) Router {
return grp.Add([]string{MethodGet}, path, handler, handlers...)
}

// Head registers a route for HEAD methods that asks for a response identical
// to that of a GET request, but without the response body.
func (grp *Group) Head(path string, handler Handler, middleware ...Handler) Router {
return grp.Add([]string{MethodHead}, path, handler, middleware...)
func (grp *Group) Head(path string, handler Handler, handlers ...Handler) Router {
return grp.Add([]string{MethodHead}, path, handler, handlers...)
}

// Post registers a route for POST methods that is used to submit an entity to the
// specified resource, often causing a change in state or side effects on the server.
func (grp *Group) Post(path string, handler Handler, middleware ...Handler) Router {
return grp.Add([]string{MethodPost}, path, handler, middleware...)
func (grp *Group) Post(path string, handler Handler, handlers ...Handler) Router {
return grp.Add([]string{MethodPost}, path, handler, handlers...)
}

// Put registers a route for PUT methods that replaces all current representations
// of the target resource with the request payload.
func (grp *Group) Put(path string, handler Handler, middleware ...Handler) Router {
return grp.Add([]string{MethodPut}, path, handler, middleware...)
func (grp *Group) Put(path string, handler Handler, handlers ...Handler) Router {
return grp.Add([]string{MethodPut}, path, handler, handlers...)
}

// Delete registers a route for DELETE methods that deletes the specified resource.
func (grp *Group) Delete(path string, handler Handler, middleware ...Handler) Router {
return grp.Add([]string{MethodDelete}, path, handler, middleware...)
func (grp *Group) Delete(path string, handler Handler, handlers ...Handler) Router {
return grp.Add([]string{MethodDelete}, path, handler, handlers...)
}

// Connect registers a route for CONNECT methods that establishes a tunnel to the
// server identified by the target resource.
func (grp *Group) Connect(path string, handler Handler, middleware ...Handler) Router {
return grp.Add([]string{MethodConnect}, path, handler, middleware...)
func (grp *Group) Connect(path string, handler Handler, handlers ...Handler) Router {
return grp.Add([]string{MethodConnect}, path, handler, handlers...)
}

// Options registers a route for OPTIONS methods that is used to describe the
// communication options for the target resource.
func (grp *Group) Options(path string, handler Handler, middleware ...Handler) Router {
return grp.Add([]string{MethodOptions}, path, handler, middleware...)
func (grp *Group) Options(path string, handler Handler, handlers ...Handler) Router {
return grp.Add([]string{MethodOptions}, path, handler, handlers...)
}

// Trace registers a route for TRACE methods that performs a message loop-back
// test along the path to the target resource.
func (grp *Group) Trace(path string, handler Handler, middleware ...Handler) Router {
return grp.Add([]string{MethodTrace}, path, handler, middleware...)
func (grp *Group) Trace(path string, handler Handler, handlers ...Handler) Router {
return grp.Add([]string{MethodTrace}, path, handler, handlers...)
}

// Patch registers a route for PATCH methods that is used to apply partial
// modifications to a resource.
func (grp *Group) Patch(path string, handler Handler, middleware ...Handler) Router {
return grp.Add([]string{MethodPatch}, path, handler, middleware...)
func (grp *Group) Patch(path string, handler Handler, handlers ...Handler) Router {
return grp.Add([]string{MethodPatch}, path, handler, handlers...)
}

// Add allows you to specify multiple HTTP methods to register a route.
func (grp *Group) Add(methods []string, path string, handler Handler, middleware ...Handler) Router {
grp.app.register(methods, getGroupPath(grp.Prefix, path), grp, handler, middleware...)
func (grp *Group) Add(methods []string, path string, handler Handler, handlers ...Handler) Router {
grp.app.register(methods, getGroupPath(grp.Prefix, path), grp, append([]Handler{handler}, handlers...)...)
if !grp.anyRouteDefined {
grp.anyRouteDefined = true
}
Expand All @@ -171,8 +171,8 @@ func (grp *Group) Add(methods []string, path string, handler Handler, middleware
}

// All will register the handler on all HTTP methods
func (grp *Group) All(path string, handler Handler, middleware ...Handler) Router {
_ = grp.Add(grp.app.config.RequestMethods, path, handler, middleware...)
func (grp *Group) All(path string, handler Handler, handlers ...Handler) Router {
_ = grp.Add(grp.app.config.RequestMethods, path, handler, handlers...)
return grp
}

Expand All @@ -183,7 +183,7 @@ func (grp *Group) All(path string, handler Handler, middleware ...Handler) Route
func (grp *Group) Group(prefix string, handlers ...Handler) Router {
prefix = getGroupPath(grp.Prefix, prefix)
if len(handlers) > 0 {
grp.app.register([]string{methodUse}, prefix, grp, nil, handlers...)
grp.app.register([]string{methodUse}, prefix, grp, handlers...)
}

// Create new group
Expand Down
8 changes: 4 additions & 4 deletions register.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ type Registering struct {
// })
//
// This method will match all HTTP verbs: GET, POST, PUT, HEAD etc...
func (r *Registering) All(handler Handler, middleware ...Handler) Register {
r.app.register([]string{methodUse}, r.path, nil, handler, middleware...)
func (r *Registering) All(handler Handler, handlers ...Handler) Register {
r.app.register([]string{methodUse}, r.path, nil, append([]Handler{handler}, handlers...)...)

Check warning on line 49 in register.go

View check run for this annotation

Codecov / codecov/patch

register.go#L48-L49

Added lines #L48 - L49 were not covered by tests
return r
}

Expand Down Expand Up @@ -105,8 +105,8 @@ func (r *Registering) Patch(handler Handler, middleware ...Handler) Register {
}

// Add allows you to specify multiple HTTP methods to register a route.
func (r *Registering) Add(methods []string, handler Handler, middleware ...Handler) Register {
r.app.register(methods, r.path, nil, handler, middleware...)
func (r *Registering) Add(methods []string, handler Handler, handlers ...Handler) Register {
r.app.register(methods, r.path, nil, append([]Handler{handler}, handlers...)...)
return r
}

Expand Down
32 changes: 16 additions & 16 deletions router.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,18 @@ import (
type Router interface {
Use(args ...any) Router

Get(path string, handler Handler, middleware ...Handler) Router
Head(path string, handler Handler, middleware ...Handler) Router
Post(path string, handler Handler, middleware ...Handler) Router
Put(path string, handler Handler, middleware ...Handler) Router
Delete(path string, handler Handler, middleware ...Handler) Router
Connect(path string, handler Handler, middleware ...Handler) Router
Options(path string, handler Handler, middleware ...Handler) Router
Trace(path string, handler Handler, middleware ...Handler) Router
Patch(path string, handler Handler, middleware ...Handler) Router

Add(methods []string, path string, handler Handler, middleware ...Handler) Router
All(path string, handler Handler, middleware ...Handler) Router
Get(path string, handler Handler, handlers ...Handler) Router
Head(path string, handler Handler, handlers ...Handler) Router
Post(path string, handler Handler, handlers ...Handler) Router
Put(path string, handler Handler, handlers ...Handler) Router
Delete(path string, handler Handler, handlers ...Handler) Router
Connect(path string, handler Handler, handlers ...Handler) Router
Options(path string, handler Handler, handlers ...Handler) Router
Trace(path string, handler Handler, handlers ...Handler) Router
Patch(path string, handler Handler, handlers ...Handler) Router

Add(methods []string, path string, handler Handler, handlers ...Handler) Router
All(path string, handler Handler, handlers ...Handler) Router

Group(prefix string, handlers ...Handler) Router

Expand Down Expand Up @@ -318,10 +318,10 @@ func (*App) copyRoute(route *Route) *Route {
}
}

func (app *App) register(methods []string, pathRaw string, group *Group, handler Handler, middleware ...Handler) {
handlers := middleware
if handler != nil {
handlers = append(handlers, handler)
func (app *App) register(methods []string, pathRaw string, group *Group, handlers ...Handler) {
// A route requires atleast one ctx handler
if group == nil && len(handlers) == 0 {
panic(fmt.Sprintf("missing handler in route: %s\n", pathRaw))

Check warning on line 324 in router.go

View check run for this annotation

Codecov / codecov/patch

router.go#L324

Added line #L324 was not covered by tests
}

// Precompute path normalization ONCE
Expand Down
33 changes: 33 additions & 0 deletions router_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,39 @@ func init() {
}
}

func Test_Route_Handler_Order(t *testing.T) {
t.Parallel()

app := New()

var order []int

handler1 := func(c Ctx) error {
order = append(order, 1)
return c.Next()
}
handler2 := func(c Ctx) error {
order = append(order, 2)
return c.Next()
}
handler3 := func(c Ctx) error {
order = append(order, 3)
return c.Next()
}

app.Get("/test", handler1, handler2, handler3, func(c Ctx) error {
order = append(order, 4)
return c.SendStatus(200)
})

resp, err := app.Test(httptest.NewRequest(MethodGet, "/test", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")

expectedOrder := []int{1, 2, 3, 4}
require.Equal(t, expectedOrder, order, "Handler order")
}

func Test_Route_Match_SameLength(t *testing.T) {
t.Parallel()

Expand Down

0 comments on commit e5ef561

Please sign in to comment.