README.md
1# gorilla/mux
2
3[![GoDoc](https://godoc.org/github.com/gorilla/mux?status.svg)](https://godoc.org/github.com/gorilla/mux)
4[![Build Status](https://travis-ci.org/gorilla/mux.svg?branch=master)](https://travis-ci.org/gorilla/mux)
5[![Sourcegraph](https://sourcegraph.com/github.com/gorilla/mux/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/mux?badge)
6
7![Gorilla Logo](http://www.gorillatoolkit.org/static/images/gorilla-icon-64.png)
8
9http://www.gorillatoolkit.org/pkg/mux
10
11Package `gorilla/mux` implements a request router and dispatcher for matching incoming requests to
12their respective handler.
13
14The name mux stands for "HTTP request multiplexer". Like the standard `http.ServeMux`, `mux.Router` matches incoming requests against a list of registered routes and calls a handler for the route that matches the URL or other conditions. The main features are:
15
16* It implements the `http.Handler` interface so it is compatible with the standard `http.ServeMux`.
17* Requests can be matched based on URL host, path, path prefix, schemes, header and query values, HTTP methods or using custom matchers.
18* URL hosts, paths and query values can have variables with an optional regular expression.
19* Registered URLs can be built, or "reversed", which helps maintaining references to resources.
20* Routes can be used as subrouters: nested routes are only tested if the parent route matches. This is useful to define groups of routes that share common conditions like a host, a path prefix or other repeated attributes. As a bonus, this optimizes request matching.
21
22---
23
24* [Install](#install)
25* [Examples](#examples)
26* [Matching Routes](#matching-routes)
27* [Static Files](#static-files)
28* [Registered URLs](#registered-urls)
29* [Walking Routes](#walking-routes)
30* [Graceful Shutdown](#graceful-shutdown)
31* [Middleware](#middleware)
32* [Testing Handlers](#testing-handlers)
33* [Full Example](#full-example)
34
35---
36
37## Install
38
39With a [correctly configured](https://golang.org/doc/install#testing) Go toolchain:
40
41```sh
42go get -u github.com/gorilla/mux
43```
44
45## Examples
46
47Let's start registering a couple of URL paths and handlers:
48
49```go
50func main() {
51 r := mux.NewRouter()
52 r.HandleFunc("/", HomeHandler)
53 r.HandleFunc("/products", ProductsHandler)
54 r.HandleFunc("/articles", ArticlesHandler)
55 http.Handle("/", r)
56}
57```
58
59Here we register three routes mapping URL paths to handlers. This is equivalent to how `http.HandleFunc()` works: if an incoming request URL matches one of the paths, the corresponding handler is called passing (`http.ResponseWriter`, `*http.Request`) as parameters.
60
61Paths can have variables. They are defined using the format `{name}` or `{name:pattern}`. If a regular expression pattern is not defined, the matched variable will be anything until the next slash. For example:
62
63```go
64r := mux.NewRouter()
65r.HandleFunc("/products/{key}", ProductHandler)
66r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler)
67r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
68```
69
70The names are used to create a map of route variables which can be retrieved calling `mux.Vars()`:
71
72```go
73func ArticlesCategoryHandler(w http.ResponseWriter, r *http.Request) {
74 vars := mux.Vars(r)
75 w.WriteHeader(http.StatusOK)
76 fmt.Fprintf(w, "Category: %v\n", vars["category"])
77}
78```
79
80And this is all you need to know about the basic usage. More advanced options are explained below.
81
82### Matching Routes
83
84Routes can also be restricted to a domain or subdomain. Just define a host pattern to be matched. They can also have variables:
85
86```go
87r := mux.NewRouter()
88// Only matches if domain is "www.example.com".
89r.Host("www.example.com")
90// Matches a dynamic subdomain.
91r.Host("{subdomain:[a-z]+}.domain.com")
92```
93
94There are several other matchers that can be added. To match path prefixes:
95
96```go
97r.PathPrefix("/products/")
98```
99
100...or HTTP methods:
101
102```go
103r.Methods("GET", "POST")
104```
105
106...or URL schemes:
107
108```go
109r.Schemes("https")
110```
111
112...or header values:
113
114```go
115r.Headers("X-Requested-With", "XMLHttpRequest")
116```
117
118...or query values:
119
120```go
121r.Queries("key", "value")
122```
123
124...or to use a custom matcher function:
125
126```go
127r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool {
128 return r.ProtoMajor == 0
129})
130```
131
132...and finally, it is possible to combine several matchers in a single route:
133
134```go
135r.HandleFunc("/products", ProductsHandler).
136 Host("www.example.com").
137 Methods("GET").
138 Schemes("http")
139```
140
141Routes are tested in the order they were added to the router. If two routes match, the first one wins:
142
143```go
144r := mux.NewRouter()
145r.HandleFunc("/specific", specificHandler)
146r.PathPrefix("/").Handler(catchAllHandler)
147```
148
149Setting the same matching conditions again and again can be boring, so we have a way to group several routes that share the same requirements. We call it "subrouting".
150
151For example, let's say we have several URLs that should only match when the host is `www.example.com`. Create a route for that host and get a "subrouter" from it:
152
153```go
154r := mux.NewRouter()
155s := r.Host("www.example.com").Subrouter()
156```
157
158Then register routes in the subrouter:
159
160```go
161s.HandleFunc("/products/", ProductsHandler)
162s.HandleFunc("/products/{key}", ProductHandler)
163s.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
164```
165
166The three URL paths we registered above will only be tested if the domain is `www.example.com`, because the subrouter is tested first. This is not only convenient, but also optimizes request matching. You can create subrouters combining any attribute matchers accepted by a route.
167
168Subrouters can be used to create domain or path "namespaces": you define subrouters in a central place and then parts of the app can register its paths relatively to a given subrouter.
169
170There's one more thing about subroutes. When a subrouter has a path prefix, the inner routes use it as base for their paths:
171
172```go
173r := mux.NewRouter()
174s := r.PathPrefix("/products").Subrouter()
175// "/products/"
176s.HandleFunc("/", ProductsHandler)
177// "/products/{key}/"
178s.HandleFunc("/{key}/", ProductHandler)
179// "/products/{key}/details"
180s.HandleFunc("/{key}/details", ProductDetailsHandler)
181```
182
183
184### Static Files
185
186Note that the path provided to `PathPrefix()` represents a "wildcard": calling
187`PathPrefix("/static/").Handler(...)` means that the handler will be passed any
188request that matches "/static/\*". This makes it easy to serve static files with mux:
189
190```go
191func main() {
192 var dir string
193
194 flag.StringVar(&dir, "dir", ".", "the directory to serve files from. Defaults to the current dir")
195 flag.Parse()
196 r := mux.NewRouter()
197
198 // This will serve files under http://localhost:8000/static/<filename>
199 r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(dir))))
200
201 srv := &http.Server{
202 Handler: r,
203 Addr: "127.0.0.1:8000",
204 // Good practice: enforce timeouts for servers you create!
205 WriteTimeout: 15 * time.Second,
206 ReadTimeout: 15 * time.Second,
207 }
208
209 log.Fatal(srv.ListenAndServe())
210}
211```
212
213### Registered URLs
214
215Now let's see how to build registered URLs.
216
217Routes can be named. All routes that define a name can have their URLs built, or "reversed". We define a name calling `Name()` on a route. For example:
218
219```go
220r := mux.NewRouter()
221r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
222 Name("article")
223```
224
225To build a URL, get the route and call the `URL()` method, passing a sequence of key/value pairs for the route variables. For the previous route, we would do:
226
227```go
228url, err := r.Get("article").URL("category", "technology", "id", "42")
229```
230
231...and the result will be a `url.URL` with the following path:
232
233```
234"/articles/technology/42"
235```
236
237This also works for host and query value variables:
238
239```go
240r := mux.NewRouter()
241r.Host("{subdomain}.domain.com").
242 Path("/articles/{category}/{id:[0-9]+}").
243 Queries("filter", "{filter}").
244 HandlerFunc(ArticleHandler).
245 Name("article")
246
247// url.String() will be "http://news.domain.com/articles/technology/42?filter=gorilla"
248url, err := r.Get("article").URL("subdomain", "news",
249 "category", "technology",
250 "id", "42",
251 "filter", "gorilla")
252```
253
254All variables defined in the route are required, and their values must conform to the corresponding patterns. These requirements guarantee that a generated URL will always match a registered route -- the only exception is for explicitly defined "build-only" routes which never match.
255
256Regex support also exists for matching Headers within a route. For example, we could do:
257
258```go
259r.HeadersRegexp("Content-Type", "application/(text|json)")
260```
261
262...and the route will match both requests with a Content-Type of `application/json` as well as `application/text`
263
264There's also a way to build only the URL host or path for a route: use the methods `URLHost()` or `URLPath()` instead. For the previous route, we would do:
265
266```go
267// "http://news.domain.com/"
268host, err := r.Get("article").URLHost("subdomain", "news")
269
270// "/articles/technology/42"
271path, err := r.Get("article").URLPath("category", "technology", "id", "42")
272```
273
274And if you use subrouters, host and path defined separately can be built as well:
275
276```go
277r := mux.NewRouter()
278s := r.Host("{subdomain}.domain.com").Subrouter()
279s.Path("/articles/{category}/{id:[0-9]+}").
280 HandlerFunc(ArticleHandler).
281 Name("article")
282
283// "http://news.domain.com/articles/technology/42"
284url, err := r.Get("article").URL("subdomain", "news",
285 "category", "technology",
286 "id", "42")
287```
288
289### Walking Routes
290
291The `Walk` function on `mux.Router` can be used to visit all of the routes that are registered on a router. For example,
292the following prints all of the registered routes:
293
294```go
295package main
296
297import (
298 "fmt"
299 "net/http"
300 "strings"
301
302 "github.com/gorilla/mux"
303)
304
305func handler(w http.ResponseWriter, r *http.Request) {
306 return
307}
308
309func main() {
310 r := mux.NewRouter()
311 r.HandleFunc("/", handler)
312 r.HandleFunc("/products", handler).Methods("POST")
313 r.HandleFunc("/articles", handler).Methods("GET")
314 r.HandleFunc("/articles/{id}", handler).Methods("GET", "PUT")
315 r.HandleFunc("/authors", handler).Queries("surname", "{surname}")
316 err := r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
317 pathTemplate, err := route.GetPathTemplate()
318 if err == nil {
319 fmt.Println("ROUTE:", pathTemplate)
320 }
321 pathRegexp, err := route.GetPathRegexp()
322 if err == nil {
323 fmt.Println("Path regexp:", pathRegexp)
324 }
325 queriesTemplates, err := route.GetQueriesTemplates()
326 if err == nil {
327 fmt.Println("Queries templates:", strings.Join(queriesTemplates, ","))
328 }
329 queriesRegexps, err := route.GetQueriesRegexp()
330 if err == nil {
331 fmt.Println("Queries regexps:", strings.Join(queriesRegexps, ","))
332 }
333 methods, err := route.GetMethods()
334 if err == nil {
335 fmt.Println("Methods:", strings.Join(methods, ","))
336 }
337 fmt.Println()
338 return nil
339 })
340
341 if err != nil {
342 fmt.Println(err)
343 }
344
345 http.Handle("/", r)
346}
347```
348
349### Graceful Shutdown
350
351Go 1.8 introduced the ability to [gracefully shutdown](https://golang.org/doc/go1.8#http_shutdown) a `*http.Server`. Here's how to do that alongside `mux`:
352
353```go
354package main
355
356import (
357 "context"
358 "flag"
359 "log"
360 "net/http"
361 "os"
362 "os/signal"
363 "time"
364
365 "github.com/gorilla/mux"
366)
367
368func main() {
369 var wait time.Duration
370 flag.DurationVar(&wait, "graceful-timeout", time.Second * 15, "the duration for which the server gracefully wait for existing connections to finish - e.g. 15s or 1m")
371 flag.Parse()
372
373 r := mux.NewRouter()
374 // Add your routes as needed
375
376 srv := &http.Server{
377 Addr: "0.0.0.0:8080",
378 // Good practice to set timeouts to avoid Slowloris attacks.
379 WriteTimeout: time.Second * 15,
380 ReadTimeout: time.Second * 15,
381 IdleTimeout: time.Second * 60,
382 Handler: r, // Pass our instance of gorilla/mux in.
383 }
384
385 // Run our server in a goroutine so that it doesn't block.
386 go func() {
387 if err := srv.ListenAndServe(); err != nil {
388 log.Println(err)
389 }
390 }()
391
392 c := make(chan os.Signal, 1)
393 // We'll accept graceful shutdowns when quit via SIGINT (Ctrl+C)
394 // SIGKILL, SIGQUIT or SIGTERM (Ctrl+/) will not be caught.
395 signal.Notify(c, os.Interrupt)
396
397 // Block until we receive our signal.
398 <-c
399
400 // Create a deadline to wait for.
401 ctx, cancel := context.WithTimeout(context.Background(), wait)
402 defer cancel()
403 // Doesn't block if no connections, but will otherwise wait
404 // until the timeout deadline.
405 srv.Shutdown(ctx)
406 // Optionally, you could run srv.Shutdown in a goroutine and block on
407 // <-ctx.Done() if your application should wait for other services
408 // to finalize based on context cancellation.
409 log.Println("shutting down")
410 os.Exit(0)
411}
412```
413
414### Middleware
415
416Mux supports the addition of middlewares to a [Router](https://godoc.org/github.com/gorilla/mux#Router), which are executed in the order they are added if a match is found, including its subrouters.
417Middlewares are (typically) small pieces of code which take one request, do something with it, and pass it down to another middleware or the final handler. Some common use cases for middleware are request logging, header manipulation, or `ResponseWriter` hijacking.
418
419Mux middlewares are defined using the de facto standard type:
420
421```go
422type MiddlewareFunc func(http.Handler) http.Handler
423```
424
425Typically, the returned handler is a closure which does something with the http.ResponseWriter and http.Request passed to it, and then calls the handler passed as parameter to the MiddlewareFunc. This takes advantage of closures being able access variables from the context where they are created, while retaining the signature enforced by the receivers.
426
427A very basic middleware which logs the URI of the request being handled could be written as:
428
429```go
430func loggingMiddleware(next http.Handler) http.Handler {
431 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
432 // Do stuff here
433 log.Println(r.RequestURI)
434 // Call the next handler, which can be another middleware in the chain, or the final handler.
435 next.ServeHTTP(w, r)
436 })
437}
438```
439
440Middlewares can be added to a router using `Router.Use()`:
441
442```go
443r := mux.NewRouter()
444r.HandleFunc("/", handler)
445r.Use(loggingMiddleware)
446```
447
448A more complex authentication middleware, which maps session token to users, could be written as:
449
450```go
451// Define our struct
452type authenticationMiddleware struct {
453 tokenUsers map[string]string
454}
455
456// Initialize it somewhere
457func (amw *authenticationMiddleware) Populate() {
458 amw.tokenUsers["00000000"] = "user0"
459 amw.tokenUsers["aaaaaaaa"] = "userA"
460 amw.tokenUsers["05f717e5"] = "randomUser"
461 amw.tokenUsers["deadbeef"] = "user0"
462}
463
464// Middleware function, which will be called for each request
465func (amw *authenticationMiddleware) Middleware(next http.Handler) http.Handler {
466 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
467 token := r.Header.Get("X-Session-Token")
468
469 if user, found := amw.tokenUsers[token]; found {
470 // We found the token in our map
471 log.Printf("Authenticated user %s\n", user)
472 // Pass down the request to the next middleware (or final handler)
473 next.ServeHTTP(w, r)
474 } else {
475 // Write an error and stop the handler chain
476 http.Error(w, "Forbidden", http.StatusForbidden)
477 }
478 })
479}
480```
481
482```go
483r := mux.NewRouter()
484r.HandleFunc("/", handler)
485
486amw := authenticationMiddleware{}
487amw.Populate()
488
489r.Use(amw.Middleware)
490```
491
492Note: The handler chain will be stopped if your middleware doesn't call `next.ServeHTTP()` with the corresponding parameters. This can be used to abort a request if the middleware writer wants to. Middlewares _should_ write to `ResponseWriter` if they _are_ going to terminate the request, and they _should not_ write to `ResponseWriter` if they _are not_ going to terminate it.
493
494### Testing Handlers
495
496Testing handlers in a Go web application is straightforward, and _mux_ doesn't complicate this any further. Given two files: `endpoints.go` and `endpoints_test.go`, here's how we'd test an application using _mux_.
497
498First, our simple HTTP handler:
499
500```go
501// endpoints.go
502package main
503
504func HealthCheckHandler(w http.ResponseWriter, r *http.Request) {
505 // A very simple health check.
506 w.WriteHeader(http.StatusOK)
507 w.Header().Set("Content-Type", "application/json")
508
509 // In the future we could report back on the status of our DB, or our cache
510 // (e.g. Redis) by performing a simple PING, and include them in the response.
511 io.WriteString(w, `{"alive": true}`)
512}
513
514func main() {
515 r := mux.NewRouter()
516 r.HandleFunc("/health", HealthCheckHandler)
517
518 log.Fatal(http.ListenAndServe("localhost:8080", r))
519}
520```
521
522Our test code:
523
524```go
525// endpoints_test.go
526package main
527
528import (
529 "net/http"
530 "net/http/httptest"
531 "testing"
532)
533
534func TestHealthCheckHandler(t *testing.T) {
535 // Create a request to pass to our handler. We don't have any query parameters for now, so we'll
536 // pass 'nil' as the third parameter.
537 req, err := http.NewRequest("GET", "/health", nil)
538 if err != nil {
539 t.Fatal(err)
540 }
541
542 // We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response.
543 rr := httptest.NewRecorder()
544 handler := http.HandlerFunc(HealthCheckHandler)
545
546 // Our handlers satisfy http.Handler, so we can call their ServeHTTP method
547 // directly and pass in our Request and ResponseRecorder.
548 handler.ServeHTTP(rr, req)
549
550 // Check the status code is what we expect.
551 if status := rr.Code; status != http.StatusOK {
552 t.Errorf("handler returned wrong status code: got %v want %v",
553 status, http.StatusOK)
554 }
555
556 // Check the response body is what we expect.
557 expected := `{"alive": true}`
558 if rr.Body.String() != expected {
559 t.Errorf("handler returned unexpected body: got %v want %v",
560 rr.Body.String(), expected)
561 }
562}
563```
564
565In the case that our routes have [variables](#examples), we can pass those in the request. We could write
566[table-driven tests](https://dave.cheney.net/2013/06/09/writing-table-driven-tests-in-go) to test multiple
567possible route variables as needed.
568
569```go
570// endpoints.go
571func main() {
572 r := mux.NewRouter()
573 // A route with a route variable:
574 r.HandleFunc("/metrics/{type}", MetricsHandler)
575
576 log.Fatal(http.ListenAndServe("localhost:8080", r))
577}
578```
579
580Our test file, with a table-driven test of `routeVariables`:
581
582```go
583// endpoints_test.go
584func TestMetricsHandler(t *testing.T) {
585 tt := []struct{
586 routeVariable string
587 shouldPass bool
588 }{
589 {"goroutines", true},
590 {"heap", true},
591 {"counters", true},
592 {"queries", true},
593 {"adhadaeqm3k", false},
594 }
595
596 for _, tc := range tt {
597 path := fmt.Sprintf("/metrics/%s", tc.routeVariable)
598 req, err := http.NewRequest("GET", path, nil)
599 if err != nil {
600 t.Fatal(err)
601 }
602
603 rr := httptest.NewRecorder()
604
605 // Need to create a router that we can pass the request through so that the vars will be added to the context
606 router := mux.NewRouter()
607 router.HandleFunc("/metrics/{type}", MetricsHandler)
608 router.ServeHTTP(rr, req)
609
610 // In this case, our MetricsHandler returns a non-200 response
611 // for a route variable it doesn't know about.
612 if rr.Code == http.StatusOK && !tc.shouldPass {
613 t.Errorf("handler should have failed on routeVariable %s: got %v want %v",
614 tc.routeVariable, rr.Code, http.StatusOK)
615 }
616 }
617}
618```
619
620## Full Example
621
622Here's a complete, runnable example of a small `mux` based server:
623
624```go
625package main
626
627import (
628 "net/http"
629 "log"
630 "github.com/gorilla/mux"
631)
632
633func YourHandler(w http.ResponseWriter, r *http.Request) {
634 w.Write([]byte("Gorilla!\n"))
635}
636
637func main() {
638 r := mux.NewRouter()
639 // Routes consist of a path and a handler function.
640 r.HandleFunc("/", YourHandler)
641
642 // Bind to a port and pass our router in
643 log.Fatal(http.ListenAndServe(":8000", r))
644}
645```
646
647## License
648
649BSD licensed. See the LICENSE file for details.
650