Skip to content
Open
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
10 changes: 6 additions & 4 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,10 +305,12 @@ type API interface {

// UseMiddleware appends a middleware handler to the API middleware stack.
//
// The middleware stack for any API will execute before searching for a matching
// route to a specific handler, which provides opportunity to respond early,
// change the course of the request execution, or set request-scoped values for
// the next Middleware.
// The middleware stack runs after the adapter's router has matched the
// request to a registered operation and before the operation handler
// executes. Requests that do not match any registered operation are
// handled by the router itself (for example with a 404 response) and do
// not pass through this middleware; use router-specific middleware for
// pre-routing logic.
UseMiddleware(middlewares ...func(ctx Context, next func(Context)))

// Middlewares returns a slice of middleware handler functions that will be
Expand Down
19 changes: 19 additions & 0 deletions huma_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4975,3 +4975,22 @@ func TestWriteResponseTransformErrorStatus(t *testing.T) {
assert.Equal(t, http.StatusInternalServerError, res.StatusCode)
assert.Contains(t, string(body), "error transforming response")
}

func TestMiddlewareDoesNotRunForUnmatchedRoute(t *testing.T) {
// Router-agnostic middleware runs after the adapter's router resolves the
// route: an unmatched route is answered by the router (404) without
// entering the middleware chain (issue #933).
_, api := humatest.New(t)
ran := false
api.UseMiddleware(func(ctx huma.Context, next func(huma.Context)) {
ran = true
next(ctx)
})
huma.Get(api, "/matched", func(ctx context.Context, input *struct{}) (*struct{}, error) {
return &struct{}{}, nil
})

res := api.Get("/unmatched")
assert.Equal(t, http.StatusNotFound, res.Code)
assert.False(t, ran, "middleware should not run for an unmatched route")
}