doc.go

  1// 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, paths and query values can have variables with an optional
 16	  regular 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 and query value variables:
192
193	r := mux.NewRouter()
194	r.Host("{subdomain}.domain.com").
195	  Path("/articles/{category}/{id:[0-9]+}").
196	  Queries("filter", "{filter}").
197	  HandlerFunc(ArticleHandler).
198	  Name("article")
199
200	// url.String() will be "http://news.domain.com/articles/technology/42?filter=gorilla"
201	url, err := r.Get("article").URL("subdomain", "news",
202	                                 "category", "technology",
203	                                 "id", "42",
204	                                 "filter", "gorilla")
205
206All variables defined in the route are required, and their values must
207conform to the corresponding patterns. These requirements guarantee that a
208generated URL will always match a registered route -- the only exception is
209for explicitly defined "build-only" routes which never match.
210
211Regex support also exists for matching Headers within a route. For example, we could do:
212
213	r.HeadersRegexp("Content-Type", "application/(text|json)")
214
215...and the route will match both requests with a Content-Type of `application/json` as well as
216`application/text`
217
218There's also a way to build only the URL host or path for a route:
219use the methods URLHost() or URLPath() instead. For the previous route,
220we would do:
221
222	// "http://news.domain.com/"
223	host, err := r.Get("article").URLHost("subdomain", "news")
224
225	// "/articles/technology/42"
226	path, err := r.Get("article").URLPath("category", "technology", "id", "42")
227
228And if you use subrouters, host and path defined separately can be built
229as well:
230
231	r := mux.NewRouter()
232	s := r.Host("{subdomain}.domain.com").Subrouter()
233	s.Path("/articles/{category}/{id:[0-9]+}").
234	  HandlerFunc(ArticleHandler).
235	  Name("article")
236
237	// "http://news.domain.com/articles/technology/42"
238	url, err := r.Get("article").URL("subdomain", "news",
239	                                 "category", "technology",
240	                                 "id", "42")
241
242Mux supports the addition of middlewares to a Router, which are executed in the order they are added if a match is found, including its subrouters. Middlewares 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.
243
244	type MiddlewareFunc func(http.Handler) http.Handler
245
246Typically, 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 (closures can access variables from the context where they are created).
247
248A very basic middleware which logs the URI of the request being handled could be written as:
249
250	func simpleMw(next http.Handler) http.Handler {
251		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
252			// Do stuff here
253			log.Println(r.RequestURI)
254			// Call the next handler, which can be another middleware in the chain, or the final handler.
255			next.ServeHTTP(w, r)
256		})
257	}
258
259Middlewares can be added to a router using `Router.Use()`:
260
261	r := mux.NewRouter()
262	r.HandleFunc("/", handler)
263	r.Use(simpleMw)
264
265A more complex authentication middleware, which maps session token to users, could be written as:
266
267	// Define our struct
268	type authenticationMiddleware struct {
269		tokenUsers map[string]string
270	}
271
272	// Initialize it somewhere
273	func (amw *authenticationMiddleware) Populate() {
274		amw.tokenUsers["00000000"] = "user0"
275		amw.tokenUsers["aaaaaaaa"] = "userA"
276		amw.tokenUsers["05f717e5"] = "randomUser"
277		amw.tokenUsers["deadbeef"] = "user0"
278	}
279
280	// Middleware function, which will be called for each request
281	func (amw *authenticationMiddleware) Middleware(next http.Handler) http.Handler {
282		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
283			token := r.Header.Get("X-Session-Token")
284
285			if user, found := amw.tokenUsers[token]; found {
286				// We found the token in our map
287				log.Printf("Authenticated user %s\n", user)
288				next.ServeHTTP(w, r)
289			} else {
290				http.Error(w, "Forbidden", http.StatusForbidden)
291			}
292		})
293	}
294
295	r := mux.NewRouter()
296	r.HandleFunc("/", handler)
297
298	amw := authenticationMiddleware{}
299	amw.Populate()
300
301	r.Use(amw.Middleware)
302
303Note: 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.
304
305*/
306package mux