• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

.travis.ymlH A D13-May-2018323 2420

ISSUE_TEMPLATE.mdH A D13-May-2018365 124

LICENSEH A D13-May-20181.4 KiB2824

README.mdH A D13-May-201820 KiB650498

bench_test.goH A D13-May-20181.4 KiB5038

context_gorilla.goH A D13-May-2018380 2718

context_gorilla_test.goH A D13-May-2018868 4125

context_native.goH A D13-May-2018380 2517

context_native_test.goH A D13-May-2018690 3326

doc.goH A D13-May-201810.9 KiB3071

example_authentication_middleware_test.goH A D13-May-20181.1 KiB4734

example_route_test.goH A D13-May-20181.7 KiB5229

middleware.goH A D13-May-20182.4 KiB7349

middleware_test.goH A D13-May-20189.4 KiB378285

mux.goH A D13-May-201817.1 KiB589348

mux_test.goH A D13-May-201874.4 KiB2,3652,163

old_test.goH A D13-May-201817.1 KiB705577

regexp.goH A D13-May-20188.8 KiB333249

route.goH A D13-May-201821.6 KiB764445

test_helpers.goH A D13-May-2018758 205

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