Skip to content

Commit bb8116c

Browse files
committed
feat(router): redirect trailing slashes
1 parent 40de397 commit bb8116c

File tree

2 files changed

+59
-146
lines changed

2 files changed

+59
-146
lines changed

foo/main.go

-124
This file was deleted.

router.go

+59-22
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,14 @@ func (router *Router) Use(middlewares ...Middleware) {
128128
router.middlewares = append(router.middlewares, middlewares...)
129129
}
130130

131+
// register adds the given pattern and handler to the actual native
132+
// router [http.ServeMux].
133+
func (router *Router) register(pattern string, handler http.Handler) {
134+
router.
135+
mux().
136+
Handle(pattern, handler)
137+
}
138+
131139
// Method registers a new handler to the router with the given
132140
// method and pattern. This is usefull if you need to dynamically
133141
// register a route to the router using a string as the method.
@@ -151,27 +159,62 @@ func (router *Router) Use(middlewares ...Middleware) {
151159
// - [http.MethodOptions]
152160
// - [http.MethodTrace]
153161
func (router *Router) Method(method string, pattern string, handler Handler) {
154-
router.
155-
mux().
156-
Handle(router.generatePattern(method, pattern), router.wrap(handler))
157-
}
158-
159-
// generatePattern creates the actual pattern that will be
160-
// registered to the mux handler.
161-
func (router *Router) generatePattern(method string, pattern string) string {
162162
pattern = path.Join(router.pattern, pattern)
163163

164-
if strings.HasSuffix(pattern, "...}") {
165-
return fmt.Sprintf("%s %s", method, pattern)
164+
if pattern == "/" {
165+
router.register(
166+
fmt.Sprintf("%s %s{$}", method, pattern),
167+
router.wrap(handler),
168+
)
169+
170+
return
166171
}
167172

168-
if !strings.HasSuffix(pattern, "/") {
169-
return fmt.Sprintf("%s %s/{$}", method, pattern)
173+
if !strings.HasSuffix("/", pattern) && !strings.HasSuffix(pattern, "...}") {
174+
// The redirection route should ONLY be registered when:
175+
// - The path does not end in "/"
176+
// - The path does not end in "...}"
177+
router.register(
178+
fmt.Sprintf("%s %s/{$}", method, pattern),
179+
router.redirect(pattern),
180+
)
170181
}
171182

172-
return fmt.Sprintf("%s %s{$}", method, pattern)
183+
router.register(
184+
fmt.Sprintf("%s %s", method, pattern),
185+
router.wrap(handler),
186+
)
173187
}
174188

189+
// redirect is a helper handler that takes care of redirecting
190+
// to the specified path while maintaining the query string.
191+
func (router *Router) redirect(path string) http.Handler {
192+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
193+
target := path
194+
if r.URL.RawQuery != "" {
195+
target += "?" + r.URL.RawQuery
196+
}
197+
198+
http.Redirect(w, r, target, http.StatusTemporaryRedirect)
199+
})
200+
}
201+
202+
// generatePattern creates the actual pattern that will be
203+
// registered to the mux handler.
204+
// func (router *Router) generatePattern(method string, pattern string) string {
205+
// pattern = path.Join(router.pattern, pattern)
206+
207+
// if strings.HasSuffix(pattern, "...}") {
208+
// return fmt.Sprintf("%s %s", method, pattern)
209+
// }
210+
211+
// if !strings.HasSuffix(pattern, "/") {
212+
// return fmt.Sprintf("%s %s/{$}", method, pattern)
213+
// }
214+
215+
// return fmt.Sprintf("%s %s{$}", method, pattern)
216+
// }
217+
175218
// Get registers a new handler to the router using [Router.Method]
176219
// and using the [http.MethodGet] as the method parameter.
177220
func (router *Router) Get(pattern string, handler Handler) {
@@ -263,7 +306,7 @@ func (router *Router) Matches(request *http.Request) bool {
263306
// The second return value determines if the [Handler] was found or not.
264307
//
265308
// For matching against an [http.Request] use the [Router.HandlerMatch] method.
266-
func (router *Router) Handler(method string, pattern string) (Handler, bool) {
309+
func (router *Router) Handler(method string, pattern string) (http.Handler, bool) {
267310
if request, err := http.NewRequest(method, pattern, nil); err == nil {
268311
return router.HandlerMatch(request)
269312
}
@@ -275,15 +318,9 @@ func (router *Router) Handler(method string, pattern string) (Handler, bool) {
275318
// The second return value determines if the [Handler] was found or not.
276319
//
277320
// For matching against a method and a pattern, use the [Router.Handler] method.
278-
func (router *Router) HandlerMatch(request *http.Request) (Handler, bool) {
279-
if !strings.HasSuffix(request.URL.Path, "/") {
280-
request.URL.Path += "/"
281-
}
282-
321+
func (router *Router) HandlerMatch(request *http.Request) (http.Handler, bool) {
283322
if handler, pattern := router.native.Handler(request); pattern != "" {
284-
if handler, ok := handler.(Handler); ok {
285-
return handler, true
286-
}
323+
return handler, true
287324
}
288325

289326
return nil, false

0 commit comments

Comments
 (0)