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.
16 // Route stores information to match a request and build URLs.
18 // Parent where the route was registered (a Router).
20 // Request handler for the route.
24 // Manager for the variables from host and path.
25 regexp *routeRegexpGroup
26 // If true, when the path pattern is "/path/", accessing "/path" will
27 // redirect to the former and vice versa.
29 // If true, this route never matches: it is only used to build URLs.
31 // The name used to build URLs.
33 // Error resulted from building a route.
36 buildVarsFunc BuildVarsFunc
39 // Match matches the route against the request.
40 func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
41 if r.buildOnly || r.err != nil {
45 for _, m := range r.matchers {
46 if matched := m.Match(req, match); !matched {
50 // Yay, we have a match. Let's collect some info about it.
51 if match.Route == nil {
54 if match.Handler == nil {
55 match.Handler = r.handler
57 if match.Vars == nil {
58 match.Vars = make(map[string]string)
62 r.regexp.setMatch(req, match, r)
67 // ----------------------------------------------------------------------------
69 // ----------------------------------------------------------------------------
71 // GetError returns an error resulted from building the route, if any.
72 func (r *Route) GetError() error {
76 // BuildOnly sets the route to never match: it is only used to build URLs.
77 func (r *Route) BuildOnly() *Route {
82 // Handler --------------------------------------------------------------------
84 // Handler sets a handler for the route.
85 func (r *Route) Handler(handler http.Handler) *Route {
92 // HandlerFunc sets a handler function for the route.
93 func (r *Route) HandlerFunc(f func(http.ResponseWriter, *http.Request)) *Route {
94 return r.Handler(http.HandlerFunc(f))
97 // GetHandler returns the handler for the route, if any.
98 func (r *Route) GetHandler() http.Handler {
102 // Name -----------------------------------------------------------------------
104 // Name sets the name for the route, used to build URLs.
105 // If the name was registered already it will be overwritten.
106 func (r *Route) Name(name string) *Route {
108 r.err = fmt.Errorf("mux: route already has name %q, can't set %q",
113 r.getNamedRoutes()[name] = r
118 // GetName returns the name for the route, if any.
119 func (r *Route) GetName() string {
123 // ----------------------------------------------------------------------------
125 // ----------------------------------------------------------------------------
127 // matcher types try to match a request.
128 type matcher interface {
129 Match(*http.Request, *RouteMatch) bool
132 // addMatcher adds a matcher to the route.
133 func (r *Route) addMatcher(m matcher) *Route {
135 r.matchers = append(r.matchers, m)
140 // addRegexpMatcher adds a host or path matcher and builder to a route.
141 func (r *Route) addRegexpMatcher(tpl string, matchHost, matchPrefix, matchQuery bool) error {
145 r.regexp = r.getRegexpGroup()
146 if !matchHost && !matchQuery {
147 if len(tpl) == 0 || tpl[0] != '/' {
148 return fmt.Errorf("mux: path must start with a slash, got %q", tpl)
150 if r.regexp.path != nil {
151 tpl = strings.TrimRight(r.regexp.path.template, "/") + tpl
154 rr, err := newRouteRegexp(tpl, matchHost, matchPrefix, matchQuery, r.strictSlash)
158 for _, q := range r.regexp.queries {
159 if err = uniqueVars(rr.varsN, q.varsN); err != nil {
164 if r.regexp.path != nil {
165 if err = uniqueVars(rr.varsN, r.regexp.path.varsN); err != nil {
171 if r.regexp.host != nil {
172 if err = uniqueVars(rr.varsN, r.regexp.host.varsN); err != nil {
177 r.regexp.queries = append(r.regexp.queries, rr)
186 // Headers --------------------------------------------------------------------
188 // headerMatcher matches the request against header values.
189 type headerMatcher map[string]string
191 func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool {
192 return matchMapWithString(m, r.Header, true)
195 // Headers adds a matcher for request header values.
196 // It accepts a sequence of key/value pairs to be matched. For example:
198 // r := mux.NewRouter()
199 // r.Headers("Content-Type", "application/json",
200 // "X-Requested-With", "XMLHttpRequest")
202 // The above route will only match if both request header values match.
203 // If the value is an empty string, it will match any value if the key is set.
204 func (r *Route) Headers(pairs ...string) *Route {
206 var headers map[string]string
207 headers, r.err = mapFromPairsToString(pairs...)
208 return r.addMatcher(headerMatcher(headers))
213 // headerRegexMatcher matches the request against the route given a regex for the header
214 type headerRegexMatcher map[string]*regexp.Regexp
216 func (m headerRegexMatcher) Match(r *http.Request, match *RouteMatch) bool {
217 return matchMapWithRegex(m, r.Header, true)
220 // Regular expressions can be used with headers as well.
221 // It accepts a sequence of key/value pairs, where the value has regex support. For example
222 // r := mux.NewRouter()
223 // r.HeadersRegexp("Content-Type", "application/(text|json)",
224 // "X-Requested-With", "XMLHttpRequest")
226 // The above route will only match if both the request header matches both regular expressions.
227 // It the value is an empty string, it will match any value if the key is set.
228 func (r *Route) HeadersRegexp(pairs ...string) *Route {
230 var headers map[string]*regexp.Regexp
231 headers, r.err = mapFromPairsToRegex(pairs...)
232 return r.addMatcher(headerRegexMatcher(headers))
237 // Host -----------------------------------------------------------------------
239 // Host adds a matcher for the URL host.
240 // It accepts a template with zero or more URL variables enclosed by {}.
241 // Variables can define an optional regexp pattern to be matched:
243 // - {name} matches anything until the next dot.
245 // - {name:pattern} matches the given regexp pattern.
249 // r := mux.NewRouter()
250 // r.Host("www.example.com")
251 // r.Host("{subdomain}.domain.com")
252 // r.Host("{subdomain:[a-z]+}.domain.com")
254 // Variable names must be unique in a given route. They can be retrieved
255 // calling mux.Vars(request).
256 func (r *Route) Host(tpl string) *Route {
257 r.err = r.addRegexpMatcher(tpl, true, false, false)
261 // MatcherFunc ----------------------------------------------------------------
263 // MatcherFunc is the function signature used by custom matchers.
264 type MatcherFunc func(*http.Request, *RouteMatch) bool
266 func (m MatcherFunc) Match(r *http.Request, match *RouteMatch) bool {
270 // MatcherFunc adds a custom function to be used as request matcher.
271 func (r *Route) MatcherFunc(f MatcherFunc) *Route {
272 return r.addMatcher(f)
275 // Methods --------------------------------------------------------------------
277 // methodMatcher matches the request against HTTP methods.
278 type methodMatcher []string
280 func (m methodMatcher) Match(r *http.Request, match *RouteMatch) bool {
281 return matchInArray(m, r.Method)
284 // Methods adds a matcher for HTTP methods.
285 // It accepts a sequence of one or more methods to be matched, e.g.:
286 // "GET", "POST", "PUT".
287 func (r *Route) Methods(methods ...string) *Route {
288 for k, v := range methods {
289 methods[k] = strings.ToUpper(v)
291 return r.addMatcher(methodMatcher(methods))
294 // Path -----------------------------------------------------------------------
296 // Path adds a matcher for the URL path.
297 // It accepts a template with zero or more URL variables enclosed by {}. The
298 // template must start with a "/".
299 // Variables can define an optional regexp pattern to be matched:
301 // - {name} matches anything until the next slash.
303 // - {name:pattern} matches the given regexp pattern.
307 // r := mux.NewRouter()
308 // r.Path("/products/").Handler(ProductsHandler)
309 // r.Path("/products/{key}").Handler(ProductsHandler)
310 // r.Path("/articles/{category}/{id:[0-9]+}").
311 // Handler(ArticleHandler)
313 // Variable names must be unique in a given route. They can be retrieved
314 // calling mux.Vars(request).
315 func (r *Route) Path(tpl string) *Route {
316 r.err = r.addRegexpMatcher(tpl, false, false, false)
320 // PathPrefix -----------------------------------------------------------------
322 // PathPrefix adds a matcher for the URL path prefix. This matches if the given
323 // template is a prefix of the full URL path. See Route.Path() for details on
326 // Note that it does not treat slashes specially ("/foobar/" will be matched by
327 // the prefix "/foo") so you may want to use a trailing slash here.
329 // Also note that the setting of Router.StrictSlash() has no effect on routes
330 // with a PathPrefix matcher.
331 func (r *Route) PathPrefix(tpl string) *Route {
332 r.err = r.addRegexpMatcher(tpl, false, true, false)
336 // Query ----------------------------------------------------------------------
338 // Queries adds a matcher for URL query values.
339 // It accepts a sequence of key/value pairs. Values may define variables.
342 // r := mux.NewRouter()
343 // r.Queries("foo", "bar", "id", "{id:[0-9]+}")
345 // The above route will only match if the URL contains the defined queries
346 // values, e.g.: ?foo=bar&id=42.
348 // It the value is an empty string, it will match any value if the key is set.
350 // Variables can define an optional regexp pattern to be matched:
352 // - {name} matches anything until the next slash.
354 // - {name:pattern} matches the given regexp pattern.
355 func (r *Route) Queries(pairs ...string) *Route {
359 "mux: number of parameters must be multiple of 2, got %v", pairs)
362 for i := 0; i < length; i += 2 {
363 if r.err = r.addRegexpMatcher(pairs[i]+"="+pairs[i+1], false, false, true); r.err != nil {
371 // Schemes --------------------------------------------------------------------
373 // schemeMatcher matches the request against URL schemes.
374 type schemeMatcher []string
376 func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool {
377 return matchInArray(m, r.URL.Scheme)
380 // Schemes adds a matcher for URL schemes.
381 // It accepts a sequence of schemes to be matched, e.g.: "http", "https".
382 func (r *Route) Schemes(schemes ...string) *Route {
383 for k, v := range schemes {
384 schemes[k] = strings.ToLower(v)
386 return r.addMatcher(schemeMatcher(schemes))
389 // BuildVarsFunc --------------------------------------------------------------
391 // BuildVarsFunc is the function signature used by custom build variable
392 // functions (which can modify route variables before a route's URL is built).
393 type BuildVarsFunc func(map[string]string) map[string]string
395 // BuildVarsFunc adds a custom function to be used to modify build variables
396 // before a route's URL is built.
397 func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route {
402 // Subrouter ------------------------------------------------------------------
404 // Subrouter creates a subrouter for the route.
406 // It will test the inner routes only if the parent route matched. For example:
408 // r := mux.NewRouter()
409 // s := r.Host("www.example.com").Subrouter()
410 // s.HandleFunc("/products/", ProductsHandler)
411 // s.HandleFunc("/products/{key}", ProductHandler)
412 // s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
414 // Here, the routes registered in the subrouter won't be tested if the host
416 func (r *Route) Subrouter() *Router {
417 router := &Router{parent: r, strictSlash: r.strictSlash}
422 // ----------------------------------------------------------------------------
424 // ----------------------------------------------------------------------------
426 // URL builds a URL for the route.
428 // It accepts a sequence of key/value pairs for the route variables. For
429 // example, given this route:
431 // r := mux.NewRouter()
432 // r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
435 // ...a URL for it can be built using:
437 // url, err := r.Get("article").URL("category", "technology", "id", "42")
439 // ...which will return an url.URL with the following path:
441 // "/articles/technology/42"
443 // This also works for host variables:
445 // r := mux.NewRouter()
446 // r.Host("{subdomain}.domain.com").
447 // HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
450 // // url.String() will be "http://news.domain.com/articles/technology/42"
451 // url, err := r.Get("article").URL("subdomain", "news",
452 // "category", "technology",
455 // All variables defined in the route are required, and their values must
456 // conform to the corresponding patterns.
457 func (r *Route) URL(pairs ...string) (*url.URL, error) {
462 return nil, errors.New("mux: route doesn't have a host or path")
464 values, err := r.prepareVars(pairs...)
468 var scheme, host, path string
469 if r.regexp.host != nil {
470 // Set a default scheme.
472 if host, err = r.regexp.host.url(values); err != nil {
476 if r.regexp.path != nil {
477 if path, err = r.regexp.path.url(values); err != nil {
488 // URLHost builds the host part of the URL for a route. See Route.URL().
490 // The route must have a host defined.
491 func (r *Route) URLHost(pairs ...string) (*url.URL, error) {
495 if r.regexp == nil || r.regexp.host == nil {
496 return nil, errors.New("mux: route doesn't have a host")
498 values, err := r.prepareVars(pairs...)
502 host, err := r.regexp.host.url(values)
512 // URLPath builds the path part of the URL for a route. See Route.URL().
514 // The route must have a path defined.
515 func (r *Route) URLPath(pairs ...string) (*url.URL, error) {
519 if r.regexp == nil || r.regexp.path == nil {
520 return nil, errors.New("mux: route doesn't have a path")
522 values, err := r.prepareVars(pairs...)
526 path, err := r.regexp.path.url(values)
535 // prepareVars converts the route variable pairs into a map. If the route has a
536 // BuildVarsFunc, it is invoked.
537 func (r *Route) prepareVars(pairs ...string) (map[string]string, error) {
538 m, err := mapFromPairsToString(pairs...)
542 return r.buildVars(m), nil
545 func (r *Route) buildVars(m map[string]string) map[string]string {
547 m = r.parent.buildVars(m)
549 if r.buildVarsFunc != nil {
550 m = r.buildVarsFunc(m)
555 // ----------------------------------------------------------------------------
557 // ----------------------------------------------------------------------------
559 // parentRoute allows routes to know about parent host and path definitions.
560 type parentRoute interface {
561 getNamedRoutes() map[string]*Route
562 getRegexpGroup() *routeRegexpGroup
563 buildVars(map[string]string) map[string]string
566 // getNamedRoutes returns the map where named routes are registered.
567 func (r *Route) getNamedRoutes() map[string]*Route {
569 // During tests router is not always set.
570 r.parent = NewRouter()
572 return r.parent.getNamedRoutes()
575 // getRegexpGroup returns regexp definitions from this route.
576 func (r *Route) getRegexpGroup() *routeRegexpGroup {
579 // During tests router is not always set.
580 r.parent = NewRouter()
582 regexp := r.parent.getRegexpGroup()
584 r.regexp = new(routeRegexpGroup)
587 r.regexp = &routeRegexpGroup{
590 queries: regexp.queries,