Skip to content

Commit b64aad3

Browse files
router: add option to save matched route path
Fixes #139 Fixes #286 Co-authored-by: Julien Schmidt <git@julienschmidt.com>
1 parent 92b4022 commit b64aad3

File tree

2 files changed

+53
-0
lines changed

2 files changed

+53
-0
lines changed

router.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,25 @@ func ParamsFromContext(ctx context.Context) Params {
122122
return p
123123
}
124124

125+
type matchKey struct{}
126+
127+
// MatchedRoutePathKey is the request context key under which the handler path
128+
// match is stored.
129+
var MatchedRoutePathKey = matchKey{}
130+
131+
// MatchedRoutePathFromContext retrieves the matched route path from the context.
132+
func MatchedRoutePathFromContext(ctx context.Context) string {
133+
p, _ := ctx.Value(MatchedRoutePathKey).(string)
134+
return p
135+
}
136+
137+
func saveMatchedRoutePathToContext(path string, handle Handle) Handle {
138+
return func(w http.ResponseWriter, req *http.Request, ps Params) {
139+
req = req.WithContext(context.WithValue(req.Context(), MatchedRoutePathKey, path))
140+
handle(w, req, ps)
141+
}
142+
}
143+
125144
// Router is a http.Handler which can be used to dispatch requests to different
126145
// handler functions via configurable routes
127146
type Router struct {
@@ -130,6 +149,10 @@ type Router struct {
130149
paramsPool sync.Pool
131150
maxParams uint16
132151

152+
// SaveMatchedRoutePathToContext when enabled adds the matched route path
153+
// onto the http.Request context before invoking the handler
154+
SaveMatchedRoutePathToContext bool
155+
133156
// Enables automatic redirection if the current route can't be matched but a
134157
// handler for the path with (without) the trailing slash exists.
135158
// For example if /foo/ is requested but a route only exists for /foo, the
@@ -268,6 +291,10 @@ func (r *Router) Handle(method, path string, handle Handle) {
268291
panic("handle must not be nil")
269292
}
270293

294+
if r.SaveMatchedRoutePathToContext {
295+
handle = saveMatchedRoutePathToContext(path, handle)
296+
}
297+
271298
if r.trees == nil {
272299
r.trees = make(map[string]*node)
273300
}

router_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,32 @@ func TestRouterParamsFromContext(t *testing.T) {
610610
}
611611
}
612612

613+
func TestRouterMatchedRouteFromContext(t *testing.T) {
614+
routed := false
615+
616+
handlerFunc := func(_ http.ResponseWriter, req *http.Request) {
617+
// get params from request context
618+
route := MatchedRoutePathFromContext(req.Context())
619+
620+
if route != "/user/:name" {
621+
t.Fatalf("Wrong matched route: want /user/:name, got %q", route)
622+
}
623+
624+
routed = true
625+
}
626+
627+
router := New()
628+
router.SaveMatchedRoutePathToContext = true
629+
router.HandlerFunc(http.MethodGet, "/user/:name", handlerFunc)
630+
631+
w := new(mockResponseWriter)
632+
r, _ := http.NewRequest(http.MethodGet, "/user/gopher", nil)
633+
router.ServeHTTP(w, r)
634+
if !routed {
635+
t.Fatal("Routing failed!")
636+
}
637+
}
638+
613639
type mockFileSystem struct {
614640
opened bool
615641
}

0 commit comments

Comments
 (0)