blob: 00daf4a721c8ef91b71eaca08e5287c309915914 [file] [log] [blame]
David K. Bainbridge528b3182017-01-23 08:51:59 -08001// Copyright 2012 The Gorilla Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5/*
6Package mux implements a request router and dispatcher.
7
8The name mux stands for "HTTP request multiplexer". Like the standard
9http.ServeMux, mux.Router matches incoming requests against a list of
10registered routes and calls a handler for the route that matches the URL
11or other conditions. The main features are:
12
13 * Requests can be matched based on URL host, path, path prefix, schemes,
14 header and query values, HTTP methods or using custom matchers.
15 * URL hosts and paths can have variables with an optional regular
16 expression.
17 * Registered URLs can be built, or "reversed", which helps maintaining
18 references to resources.
19 * Routes can be used as subrouters: nested routes are only tested if the
20 parent route matches. This is useful to define groups of routes that
21 share common conditions like a host, a path prefix or other repeated
22 attributes. As a bonus, this optimizes request matching.
23 * It implements the http.Handler interface so it is compatible with the
24 standard http.ServeMux.
25
26Let's start registering a couple of URL paths and handlers:
27
28 func main() {
29 r := mux.NewRouter()
30 r.HandleFunc("/", HomeHandler)
31 r.HandleFunc("/products", ProductsHandler)
32 r.HandleFunc("/articles", ArticlesHandler)
33 http.Handle("/", r)
34 }
35
36Here we register three routes mapping URL paths to handlers. This is
37equivalent to how http.HandleFunc() works: if an incoming request URL matches
38one of the paths, the corresponding handler is called passing
39(http.ResponseWriter, *http.Request) as parameters.
40
41Paths can have variables. They are defined using the format {name} or
42{name:pattern}. If a regular expression pattern is not defined, the matched
43variable will be anything until the next slash. For example:
44
45 r := mux.NewRouter()
46 r.HandleFunc("/products/{key}", ProductHandler)
47 r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler)
48 r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
49
50Groups can be used inside patterns, as long as they are non-capturing (?:re). For example:
51
52 r.HandleFunc("/articles/{category}/{sort:(?:asc|desc|new)}", ArticlesCategoryHandler)
53
54The names are used to create a map of route variables which can be retrieved
55calling mux.Vars():
56
57 vars := mux.Vars(request)
58 category := vars["category"]
59
60Note that if any capturing groups are present, mux will panic() during parsing. To prevent
61this, convert any capturing groups to non-capturing, e.g. change "/{sort:(asc|desc)}" to
62"/{sort:(?:asc|desc)}". This is a change from prior versions which behaved unpredictably
63when capturing groups were present.
64
65And this is all you need to know about the basic usage. More advanced options
66are explained below.
67
68Routes can also be restricted to a domain or subdomain. Just define a host
69pattern to be matched. They can also have variables:
70
71 r := mux.NewRouter()
72 // Only matches if domain is "www.example.com".
73 r.Host("www.example.com")
74 // Matches a dynamic subdomain.
75 r.Host("{subdomain:[a-z]+}.domain.com")
76
77There are several other matchers that can be added. To match path prefixes:
78
79 r.PathPrefix("/products/")
80
81...or HTTP methods:
82
83 r.Methods("GET", "POST")
84
85...or URL schemes:
86
87 r.Schemes("https")
88
89...or header values:
90
91 r.Headers("X-Requested-With", "XMLHttpRequest")
92
93...or query values:
94
95 r.Queries("key", "value")
96
97...or to use a custom matcher function:
98
99 r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool {
100 return r.ProtoMajor == 0
101 })
102
103...and finally, it is possible to combine several matchers in a single route:
104
105 r.HandleFunc("/products", ProductsHandler).
106 Host("www.example.com").
107 Methods("GET").
108 Schemes("http")
109
110Setting the same matching conditions again and again can be boring, so we have
111a way to group several routes that share the same requirements.
112We call it "subrouting".
113
114For example, let's say we have several URLs that should only match when the
115host is "www.example.com". Create a route for that host and get a "subrouter"
116from it:
117
118 r := mux.NewRouter()
119 s := r.Host("www.example.com").Subrouter()
120
121Then register routes in the subrouter:
122
123 s.HandleFunc("/products/", ProductsHandler)
124 s.HandleFunc("/products/{key}", ProductHandler)
125 s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
126
127The three URL paths we registered above will only be tested if the domain is
128"www.example.com", because the subrouter is tested first. This is not
129only convenient, but also optimizes request matching. You can create
130subrouters combining any attribute matchers accepted by a route.
131
132Subrouters can be used to create domain or path "namespaces": you define
133subrouters in a central place and then parts of the app can register its
134paths relatively to a given subrouter.
135
136There's one more thing about subroutes. When a subrouter has a path prefix,
137the inner routes use it as base for their paths:
138
139 r := mux.NewRouter()
140 s := r.PathPrefix("/products").Subrouter()
141 // "/products/"
142 s.HandleFunc("/", ProductsHandler)
143 // "/products/{key}/"
144 s.HandleFunc("/{key}/", ProductHandler)
145 // "/products/{key}/details"
146 s.HandleFunc("/{key}/details", ProductDetailsHandler)
147
148Note that the path provided to PathPrefix() represents a "wildcard": calling
149PathPrefix("/static/").Handler(...) means that the handler will be passed any
150request that matches "/static/*". This makes it easy to serve static files with mux:
151
152 func main() {
153 var dir string
154
155 flag.StringVar(&dir, "dir", ".", "the directory to serve files from. Defaults to the current dir")
156 flag.Parse()
157 r := mux.NewRouter()
158
159 // This will serve files under http://localhost:8000/static/<filename>
160 r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(dir))))
161
162 srv := &http.Server{
163 Handler: r,
164 Addr: "127.0.0.1:8000",
165 // Good practice: enforce timeouts for servers you create!
166 WriteTimeout: 15 * time.Second,
167 ReadTimeout: 15 * time.Second,
168 }
169
170 log.Fatal(srv.ListenAndServe())
171 }
172
173Now let's see how to build registered URLs.
174
175Routes can be named. All routes that define a name can have their URLs built,
176or "reversed". We define a name calling Name() on a route. For example:
177
178 r := mux.NewRouter()
179 r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
180 Name("article")
181
182To build a URL, get the route and call the URL() method, passing a sequence of
183key/value pairs for the route variables. For the previous route, we would do:
184
185 url, err := r.Get("article").URL("category", "technology", "id", "42")
186
187...and the result will be a url.URL with the following path:
188
189 "/articles/technology/42"
190
191This also works for host variables:
192
193 r := mux.NewRouter()
194 r.Host("{subdomain}.domain.com").
195 Path("/articles/{category}/{id:[0-9]+}").
196 HandlerFunc(ArticleHandler).
197 Name("article")
198
199 // url.String() will be "http://news.domain.com/articles/technology/42"
200 url, err := r.Get("article").URL("subdomain", "news",
201 "category", "technology",
202 "id", "42")
203
204All variables defined in the route are required, and their values must
205conform to the corresponding patterns. These requirements guarantee that a
206generated URL will always match a registered route -- the only exception is
207for explicitly defined "build-only" routes which never match.
208
209Regex support also exists for matching Headers within a route. For example, we could do:
210
211 r.HeadersRegexp("Content-Type", "application/(text|json)")
212
213...and the route will match both requests with a Content-Type of `application/json` as well as
214`application/text`
215
216There's also a way to build only the URL host or path for a route:
217use the methods URLHost() or URLPath() instead. For the previous route,
218we would do:
219
220 // "http://news.domain.com/"
221 host, err := r.Get("article").URLHost("subdomain", "news")
222
223 // "/articles/technology/42"
224 path, err := r.Get("article").URLPath("category", "technology", "id", "42")
225
226And if you use subrouters, host and path defined separately can be built
227as well:
228
229 r := mux.NewRouter()
230 s := r.Host("{subdomain}.domain.com").Subrouter()
231 s.Path("/articles/{category}/{id:[0-9]+}").
232 HandlerFunc(ArticleHandler).
233 Name("article")
234
235 // "http://news.domain.com/articles/technology/42"
236 url, err := r.Get("article").URL("subdomain", "news",
237 "category", "technology",
238 "id", "42")
239*/
240package mux