...

Source file src/github.com/gorilla/mux/route.go

Documentation: github.com/gorilla/mux

     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  package mux
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"net/http"
    11  	"net/url"
    12  	"regexp"
    13  	"strings"
    14  )
    15  
    16  // Route stores information to match a request and build URLs.
    17  type Route struct {
    18  	// Request handler for the route.
    19  	handler http.Handler
    20  	// If true, this route never matches: it is only used to build URLs.
    21  	buildOnly bool
    22  	// The name used to build URLs.
    23  	name string
    24  	// Error resulted from building a route.
    25  	err error
    26  
    27  	// "global" reference to all named routes
    28  	namedRoutes map[string]*Route
    29  
    30  	// config possibly passed in from `Router`
    31  	routeConf
    32  }
    33  
    34  // SkipClean reports whether path cleaning is enabled for this route via
    35  // Router.SkipClean.
    36  func (r *Route) SkipClean() bool {
    37  	return r.skipClean
    38  }
    39  
    40  // Match matches the route against the request.
    41  func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
    42  	if r.buildOnly || r.err != nil {
    43  		return false
    44  	}
    45  
    46  	var matchErr error
    47  
    48  	// Match everything.
    49  	for _, m := range r.matchers {
    50  		if matched := m.Match(req, match); !matched {
    51  			if _, ok := m.(methodMatcher); ok {
    52  				matchErr = ErrMethodMismatch
    53  				continue
    54  			}
    55  
    56  			// Ignore ErrNotFound errors. These errors arise from match call
    57  			// to Subrouters.
    58  			//
    59  			// This prevents subsequent matching subrouters from failing to
    60  			// run middleware. If not ignored, the middleware would see a
    61  			// non-nil MatchErr and be skipped, even when there was a
    62  			// matching route.
    63  			if match.MatchErr == ErrNotFound {
    64  				match.MatchErr = nil
    65  			}
    66  
    67  			matchErr = nil // nolint:ineffassign
    68  			return false
    69  		} else {
    70  			// Multiple routes may share the same path but use different HTTP methods. For instance:
    71  			// Route 1: POST "/users/{id}".
    72  			// Route 2: GET "/users/{id}", parameters: "id": "[0-9]+".
    73  			//
    74  			// The router must handle these cases correctly. For a GET request to "/users/abc" with "id" as "-2",
    75  			// The router should return a "Not Found" error as no route fully matches this request.
    76  			if match.MatchErr == ErrMethodMismatch {
    77  				match.MatchErr = nil
    78  			}
    79  		}
    80  	}
    81  
    82  	if matchErr != nil {
    83  		match.MatchErr = matchErr
    84  		return false
    85  	}
    86  
    87  	if match.MatchErr == ErrMethodMismatch && r.handler != nil {
    88  		// We found a route which matches request method, clear MatchErr
    89  		match.MatchErr = nil
    90  		// Then override the mis-matched handler
    91  		match.Handler = r.handler
    92  	}
    93  
    94  	// Yay, we have a match. Let's collect some info about it.
    95  	if match.Route == nil {
    96  		match.Route = r
    97  	}
    98  	if match.Handler == nil {
    99  		match.Handler = r.handler
   100  	}
   101  	if match.Vars == nil {
   102  		match.Vars = make(map[string]string)
   103  	}
   104  
   105  	// Set variables.
   106  	r.regexp.setMatch(req, match, r)
   107  	return true
   108  }
   109  
   110  // ----------------------------------------------------------------------------
   111  // Route attributes
   112  // ----------------------------------------------------------------------------
   113  
   114  // GetError returns an error resulted from building the route, if any.
   115  func (r *Route) GetError() error {
   116  	return r.err
   117  }
   118  
   119  // BuildOnly sets the route to never match: it is only used to build URLs.
   120  func (r *Route) BuildOnly() *Route {
   121  	r.buildOnly = true
   122  	return r
   123  }
   124  
   125  // Handler --------------------------------------------------------------------
   126  
   127  // Handler sets a handler for the route.
   128  func (r *Route) Handler(handler http.Handler) *Route {
   129  	if r.err == nil {
   130  		r.handler = handler
   131  	}
   132  	return r
   133  }
   134  
   135  // HandlerFunc sets a handler function for the route.
   136  func (r *Route) HandlerFunc(f func(http.ResponseWriter, *http.Request)) *Route {
   137  	return r.Handler(http.HandlerFunc(f))
   138  }
   139  
   140  // GetHandler returns the handler for the route, if any.
   141  func (r *Route) GetHandler() http.Handler {
   142  	return r.handler
   143  }
   144  
   145  // Name -----------------------------------------------------------------------
   146  
   147  // Name sets the name for the route, used to build URLs.
   148  // It is an error to call Name more than once on a route.
   149  func (r *Route) Name(name string) *Route {
   150  	if r.name != "" {
   151  		r.err = fmt.Errorf("mux: route already has name %q, can't set %q",
   152  			r.name, name)
   153  	}
   154  	if r.err == nil {
   155  		r.name = name
   156  		r.namedRoutes[name] = r
   157  	}
   158  	return r
   159  }
   160  
   161  // GetName returns the name for the route, if any.
   162  func (r *Route) GetName() string {
   163  	return r.name
   164  }
   165  
   166  // ----------------------------------------------------------------------------
   167  // Matchers
   168  // ----------------------------------------------------------------------------
   169  
   170  // matcher types try to match a request.
   171  type matcher interface {
   172  	Match(*http.Request, *RouteMatch) bool
   173  }
   174  
   175  // addMatcher adds a matcher to the route.
   176  func (r *Route) addMatcher(m matcher) *Route {
   177  	if r.err == nil {
   178  		r.matchers = append(r.matchers, m)
   179  	}
   180  	return r
   181  }
   182  
   183  // addRegexpMatcher adds a host or path matcher and builder to a route.
   184  func (r *Route) addRegexpMatcher(tpl string, typ regexpType) error {
   185  	if r.err != nil {
   186  		return r.err
   187  	}
   188  	if typ == regexpTypePath || typ == regexpTypePrefix {
   189  		if len(tpl) > 0 && tpl[0] != '/' {
   190  			return fmt.Errorf("mux: path must start with a slash, got %q", tpl)
   191  		}
   192  		if r.regexp.path != nil {
   193  			tpl = strings.TrimRight(r.regexp.path.template, "/") + tpl
   194  		}
   195  	}
   196  	rr, err := newRouteRegexp(tpl, typ, routeRegexpOptions{
   197  		strictSlash:    r.strictSlash,
   198  		useEncodedPath: r.useEncodedPath,
   199  	})
   200  	if err != nil {
   201  		return err
   202  	}
   203  	for _, q := range r.regexp.queries {
   204  		if err = uniqueVars(rr.varsN, q.varsN); err != nil {
   205  			return err
   206  		}
   207  	}
   208  	if typ == regexpTypeHost {
   209  		if r.regexp.path != nil {
   210  			if err = uniqueVars(rr.varsN, r.regexp.path.varsN); err != nil {
   211  				return err
   212  			}
   213  		}
   214  		r.regexp.host = rr
   215  	} else {
   216  		if r.regexp.host != nil {
   217  			if err = uniqueVars(rr.varsN, r.regexp.host.varsN); err != nil {
   218  				return err
   219  			}
   220  		}
   221  		if typ == regexpTypeQuery {
   222  			r.regexp.queries = append(r.regexp.queries, rr)
   223  		} else {
   224  			r.regexp.path = rr
   225  		}
   226  	}
   227  	r.addMatcher(rr)
   228  	return nil
   229  }
   230  
   231  // Headers --------------------------------------------------------------------
   232  
   233  // headerMatcher matches the request against header values.
   234  type headerMatcher map[string]string
   235  
   236  func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool {
   237  	return matchMapWithString(m, r.Header, true)
   238  }
   239  
   240  // Headers adds a matcher for request header values.
   241  // It accepts a sequence of key/value pairs to be matched. For example:
   242  //
   243  //	r := mux.NewRouter().NewRoute()
   244  //	r.Headers("Content-Type", "application/json",
   245  //	          "X-Requested-With", "XMLHttpRequest")
   246  //
   247  // The above route will only match if both request header values match.
   248  // If the value is an empty string, it will match any value if the key is set.
   249  func (r *Route) Headers(pairs ...string) *Route {
   250  	if r.err == nil {
   251  		var headers map[string]string
   252  		headers, r.err = mapFromPairsToString(pairs...)
   253  		return r.addMatcher(headerMatcher(headers))
   254  	}
   255  	return r
   256  }
   257  
   258  // headerRegexMatcher matches the request against the route given a regex for the header
   259  type headerRegexMatcher map[string]*regexp.Regexp
   260  
   261  func (m headerRegexMatcher) Match(r *http.Request, match *RouteMatch) bool {
   262  	return matchMapWithRegex(m, r.Header, true)
   263  }
   264  
   265  // HeadersRegexp accepts a sequence of key/value pairs, where the value has regex
   266  // support. For example:
   267  //
   268  //	r := mux.NewRouter().NewRoute()
   269  //	r.HeadersRegexp("Content-Type", "application/(text|json)",
   270  //	          "X-Requested-With", "XMLHttpRequest")
   271  //
   272  // The above route will only match if both the request header matches both regular expressions.
   273  // If the value is an empty string, it will match any value if the key is set.
   274  // Use the start and end of string anchors (^ and $) to match an exact value.
   275  func (r *Route) HeadersRegexp(pairs ...string) *Route {
   276  	if r.err == nil {
   277  		var headers map[string]*regexp.Regexp
   278  		headers, r.err = mapFromPairsToRegex(pairs...)
   279  		return r.addMatcher(headerRegexMatcher(headers))
   280  	}
   281  	return r
   282  }
   283  
   284  // Host -----------------------------------------------------------------------
   285  
   286  // Host adds a matcher for the URL host.
   287  // It accepts a template with zero or more URL variables enclosed by {}.
   288  // Variables can define an optional regexp pattern to be matched:
   289  //
   290  // - {name} matches anything until the next dot.
   291  //
   292  // - {name:pattern} matches the given regexp pattern.
   293  //
   294  // For example:
   295  //
   296  //	r := mux.NewRouter().NewRoute()
   297  //	r.Host("www.example.com")
   298  //	r.Host("{subdomain}.domain.com")
   299  //	r.Host("{subdomain:[a-z]+}.domain.com")
   300  //
   301  // Variable names must be unique in a given route. They can be retrieved
   302  // calling mux.Vars(request).
   303  func (r *Route) Host(tpl string) *Route {
   304  	r.err = r.addRegexpMatcher(tpl, regexpTypeHost)
   305  	return r
   306  }
   307  
   308  // MatcherFunc ----------------------------------------------------------------
   309  
   310  // MatcherFunc is the function signature used by custom matchers.
   311  type MatcherFunc func(*http.Request, *RouteMatch) bool
   312  
   313  // Match returns the match for a given request.
   314  func (m MatcherFunc) Match(r *http.Request, match *RouteMatch) bool {
   315  	return m(r, match)
   316  }
   317  
   318  // MatcherFunc adds a custom function to be used as request matcher.
   319  func (r *Route) MatcherFunc(f MatcherFunc) *Route {
   320  	return r.addMatcher(f)
   321  }
   322  
   323  // Methods --------------------------------------------------------------------
   324  
   325  // methodMatcher matches the request against HTTP methods.
   326  type methodMatcher []string
   327  
   328  func (m methodMatcher) Match(r *http.Request, match *RouteMatch) bool {
   329  	return matchInArray(m, r.Method)
   330  }
   331  
   332  // Methods adds a matcher for HTTP methods.
   333  // It accepts a sequence of one or more methods to be matched, e.g.:
   334  // "GET", "POST", "PUT".
   335  func (r *Route) Methods(methods ...string) *Route {
   336  	for k, v := range methods {
   337  		methods[k] = strings.ToUpper(v)
   338  	}
   339  	return r.addMatcher(methodMatcher(methods))
   340  }
   341  
   342  // Path -----------------------------------------------------------------------
   343  
   344  // Path adds a matcher for the URL path.
   345  // It accepts a template with zero or more URL variables enclosed by {}. The
   346  // template must start with a "/".
   347  // Variables can define an optional regexp pattern to be matched:
   348  //
   349  // - {name} matches anything until the next slash.
   350  //
   351  // - {name:pattern} matches the given regexp pattern.
   352  //
   353  // For example:
   354  //
   355  //	r := mux.NewRouter().NewRoute()
   356  //	r.Path("/products/").Handler(ProductsHandler)
   357  //	r.Path("/products/{key}").Handler(ProductsHandler)
   358  //	r.Path("/articles/{category}/{id:[0-9]+}").
   359  //	  Handler(ArticleHandler)
   360  //
   361  // Variable names must be unique in a given route. They can be retrieved
   362  // calling mux.Vars(request).
   363  func (r *Route) Path(tpl string) *Route {
   364  	r.err = r.addRegexpMatcher(tpl, regexpTypePath)
   365  	return r
   366  }
   367  
   368  // PathPrefix -----------------------------------------------------------------
   369  
   370  // PathPrefix adds a matcher for the URL path prefix. This matches if the given
   371  // template is a prefix of the full URL path. See Route.Path() for details on
   372  // the tpl argument.
   373  //
   374  // Note that it does not treat slashes specially ("/foobar/" will be matched by
   375  // the prefix "/foo") so you may want to use a trailing slash here.
   376  //
   377  // Also note that the setting of Router.StrictSlash() has no effect on routes
   378  // with a PathPrefix matcher.
   379  func (r *Route) PathPrefix(tpl string) *Route {
   380  	r.err = r.addRegexpMatcher(tpl, regexpTypePrefix)
   381  	return r
   382  }
   383  
   384  // Query ----------------------------------------------------------------------
   385  
   386  // Queries adds a matcher for URL query values.
   387  // It accepts a sequence of key/value pairs. Values may define variables.
   388  // For example:
   389  //
   390  //	r := mux.NewRouter().NewRoute()
   391  //	r.Queries("foo", "bar", "id", "{id:[0-9]+}")
   392  //
   393  // The above route will only match if the URL contains the defined queries
   394  // values, e.g.: ?foo=bar&id=42.
   395  //
   396  // If the value is an empty string, it will match any value if the key is set.
   397  //
   398  // Variables can define an optional regexp pattern to be matched:
   399  //
   400  // - {name} matches anything until the next slash.
   401  //
   402  // - {name:pattern} matches the given regexp pattern.
   403  func (r *Route) Queries(pairs ...string) *Route {
   404  	length := len(pairs)
   405  	if length%2 != 0 {
   406  		r.err = fmt.Errorf(
   407  			"mux: number of parameters must be multiple of 2, got %v", pairs)
   408  		return nil
   409  	}
   410  	for i := 0; i < length; i += 2 {
   411  		if r.err = r.addRegexpMatcher(pairs[i]+"="+pairs[i+1], regexpTypeQuery); r.err != nil {
   412  			return r
   413  		}
   414  	}
   415  
   416  	return r
   417  }
   418  
   419  // Schemes --------------------------------------------------------------------
   420  
   421  // schemeMatcher matches the request against URL schemes.
   422  type schemeMatcher []string
   423  
   424  func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool {
   425  	scheme := r.URL.Scheme
   426  	// https://golang.org/pkg/net/http/#Request
   427  	// "For [most] server requests, fields other than Path and RawQuery will be
   428  	// empty."
   429  	// Since we're an http muxer, the scheme is either going to be http or https
   430  	// though, so we can just set it based on the tls termination state.
   431  	if scheme == "" {
   432  		if r.TLS == nil {
   433  			scheme = "http"
   434  		} else {
   435  			scheme = "https"
   436  		}
   437  	}
   438  	return matchInArray(m, scheme)
   439  }
   440  
   441  // Schemes adds a matcher for URL schemes.
   442  // It accepts a sequence of schemes to be matched, e.g.: "http", "https".
   443  // If the request's URL has a scheme set, it will be matched against.
   444  // Generally, the URL scheme will only be set if a previous handler set it,
   445  // such as the ProxyHeaders handler from gorilla/handlers.
   446  // If unset, the scheme will be determined based on the request's TLS
   447  // termination state.
   448  // The first argument to Schemes will be used when constructing a route URL.
   449  func (r *Route) Schemes(schemes ...string) *Route {
   450  	for k, v := range schemes {
   451  		schemes[k] = strings.ToLower(v)
   452  	}
   453  	if len(schemes) > 0 {
   454  		r.buildScheme = schemes[0]
   455  	}
   456  	return r.addMatcher(schemeMatcher(schemes))
   457  }
   458  
   459  // BuildVarsFunc --------------------------------------------------------------
   460  
   461  // BuildVarsFunc is the function signature used by custom build variable
   462  // functions (which can modify route variables before a route's URL is built).
   463  type BuildVarsFunc func(map[string]string) map[string]string
   464  
   465  // BuildVarsFunc adds a custom function to be used to modify build variables
   466  // before a route's URL is built.
   467  func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route {
   468  	if r.buildVarsFunc != nil {
   469  		// compose the old and new functions
   470  		old := r.buildVarsFunc
   471  		r.buildVarsFunc = func(m map[string]string) map[string]string {
   472  			return f(old(m))
   473  		}
   474  	} else {
   475  		r.buildVarsFunc = f
   476  	}
   477  	return r
   478  }
   479  
   480  // Subrouter ------------------------------------------------------------------
   481  
   482  // Subrouter creates a subrouter for the route.
   483  //
   484  // It will test the inner routes only if the parent route matched. For example:
   485  //
   486  //	r := mux.NewRouter().NewRoute()
   487  //	s := r.Host("www.example.com").Subrouter()
   488  //	s.HandleFunc("/products/", ProductsHandler)
   489  //	s.HandleFunc("/products/{key}", ProductHandler)
   490  //	s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
   491  //
   492  // Here, the routes registered in the subrouter won't be tested if the host
   493  // doesn't match.
   494  func (r *Route) Subrouter() *Router {
   495  	// initialize a subrouter with a copy of the parent route's configuration
   496  	router := &Router{routeConf: copyRouteConf(r.routeConf), namedRoutes: r.namedRoutes}
   497  	r.addMatcher(router)
   498  	return router
   499  }
   500  
   501  // ----------------------------------------------------------------------------
   502  // URL building
   503  // ----------------------------------------------------------------------------
   504  
   505  // URL builds a URL for the route.
   506  //
   507  // It accepts a sequence of key/value pairs for the route variables. For
   508  // example, given this route:
   509  //
   510  //	r := mux.NewRouter()
   511  //	r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
   512  //	  Name("article")
   513  //
   514  // ...a URL for it can be built using:
   515  //
   516  //	url, err := r.Get("article").URL("category", "technology", "id", "42")
   517  //
   518  // ...which will return an url.URL with the following path:
   519  //
   520  //	"/articles/technology/42"
   521  //
   522  // This also works for host variables:
   523  //
   524  //	r := mux.NewRouter()
   525  //	r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
   526  //	  Host("{subdomain}.domain.com").
   527  //	  Name("article")
   528  //
   529  //	// url.String() will be "http://news.domain.com/articles/technology/42"
   530  //	url, err := r.Get("article").URL("subdomain", "news",
   531  //	                                 "category", "technology",
   532  //	                                 "id", "42")
   533  //
   534  // The scheme of the resulting url will be the first argument that was passed to Schemes:
   535  //
   536  //	// url.String() will be "https://example.com"
   537  //	r := mux.NewRouter().NewRoute()
   538  //	url, err := r.Host("example.com")
   539  //	             .Schemes("https", "http").URL()
   540  //
   541  // All variables defined in the route are required, and their values must
   542  // conform to the corresponding patterns.
   543  func (r *Route) URL(pairs ...string) (*url.URL, error) {
   544  	if r.err != nil {
   545  		return nil, r.err
   546  	}
   547  	values, err := r.prepareVars(pairs...)
   548  	if err != nil {
   549  		return nil, err
   550  	}
   551  	var scheme, host, path string
   552  	queries := make([]string, 0, len(r.regexp.queries))
   553  	if r.regexp.host != nil {
   554  		if host, err = r.regexp.host.url(values); err != nil {
   555  			return nil, err
   556  		}
   557  		scheme = "http"
   558  		if r.buildScheme != "" {
   559  			scheme = r.buildScheme
   560  		}
   561  	}
   562  	if r.regexp.path != nil {
   563  		if path, err = r.regexp.path.url(values); err != nil {
   564  			return nil, err
   565  		}
   566  	}
   567  	for _, q := range r.regexp.queries {
   568  		var query string
   569  		if query, err = q.url(values); err != nil {
   570  			return nil, err
   571  		}
   572  		queries = append(queries, query)
   573  	}
   574  	return &url.URL{
   575  		Scheme:   scheme,
   576  		Host:     host,
   577  		Path:     path,
   578  		RawQuery: strings.Join(queries, "&"),
   579  	}, nil
   580  }
   581  
   582  // URLHost builds the host part of the URL for a route. See Route.URL().
   583  //
   584  // The route must have a host defined.
   585  func (r *Route) URLHost(pairs ...string) (*url.URL, error) {
   586  	if r.err != nil {
   587  		return nil, r.err
   588  	}
   589  	if r.regexp.host == nil {
   590  		return nil, errors.New("mux: route doesn't have a host")
   591  	}
   592  	values, err := r.prepareVars(pairs...)
   593  	if err != nil {
   594  		return nil, err
   595  	}
   596  	host, err := r.regexp.host.url(values)
   597  	if err != nil {
   598  		return nil, err
   599  	}
   600  	u := &url.URL{
   601  		Scheme: "http",
   602  		Host:   host,
   603  	}
   604  	if r.buildScheme != "" {
   605  		u.Scheme = r.buildScheme
   606  	}
   607  	return u, nil
   608  }
   609  
   610  // URLPath builds the path part of the URL for a route. See Route.URL().
   611  //
   612  // The route must have a path defined.
   613  func (r *Route) URLPath(pairs ...string) (*url.URL, error) {
   614  	if r.err != nil {
   615  		return nil, r.err
   616  	}
   617  	if r.regexp.path == nil {
   618  		return nil, errors.New("mux: route doesn't have a path")
   619  	}
   620  	values, err := r.prepareVars(pairs...)
   621  	if err != nil {
   622  		return nil, err
   623  	}
   624  	path, err := r.regexp.path.url(values)
   625  	if err != nil {
   626  		return nil, err
   627  	}
   628  	return &url.URL{
   629  		Path: path,
   630  	}, nil
   631  }
   632  
   633  // GetPathTemplate returns the template used to build the
   634  // route match.
   635  // This is useful for building simple REST API documentation and for instrumentation
   636  // against third-party services.
   637  // An error will be returned if the route does not define a path.
   638  func (r *Route) GetPathTemplate() (string, error) {
   639  	if r.err != nil {
   640  		return "", r.err
   641  	}
   642  	if r.regexp.path == nil {
   643  		return "", errors.New("mux: route doesn't have a path")
   644  	}
   645  	return r.regexp.path.template, nil
   646  }
   647  
   648  // GetPathRegexp returns the expanded regular expression used to match route path.
   649  // This is useful for building simple REST API documentation and for instrumentation
   650  // against third-party services.
   651  // An error will be returned if the route does not define a path.
   652  func (r *Route) GetPathRegexp() (string, error) {
   653  	if r.err != nil {
   654  		return "", r.err
   655  	}
   656  	if r.regexp.path == nil {
   657  		return "", errors.New("mux: route does not have a path")
   658  	}
   659  	return r.regexp.path.regexp.String(), nil
   660  }
   661  
   662  // GetQueriesRegexp returns the expanded regular expressions used to match the
   663  // route queries.
   664  // This is useful for building simple REST API documentation and for instrumentation
   665  // against third-party services.
   666  // An error will be returned if the route does not have queries.
   667  func (r *Route) GetQueriesRegexp() ([]string, error) {
   668  	if r.err != nil {
   669  		return nil, r.err
   670  	}
   671  	if r.regexp.queries == nil {
   672  		return nil, errors.New("mux: route doesn't have queries")
   673  	}
   674  	queries := make([]string, 0, len(r.regexp.queries))
   675  	for _, query := range r.regexp.queries {
   676  		queries = append(queries, query.regexp.String())
   677  	}
   678  	return queries, nil
   679  }
   680  
   681  // GetQueriesTemplates returns the templates used to build the
   682  // query matching.
   683  // This is useful for building simple REST API documentation and for instrumentation
   684  // against third-party services.
   685  // An error will be returned if the route does not define queries.
   686  func (r *Route) GetQueriesTemplates() ([]string, error) {
   687  	if r.err != nil {
   688  		return nil, r.err
   689  	}
   690  	if r.regexp.queries == nil {
   691  		return nil, errors.New("mux: route doesn't have queries")
   692  	}
   693  	queries := make([]string, 0, len(r.regexp.queries))
   694  	for _, query := range r.regexp.queries {
   695  		queries = append(queries, query.template)
   696  	}
   697  	return queries, nil
   698  }
   699  
   700  // GetMethods returns the methods the route matches against
   701  // This is useful for building simple REST API documentation and for instrumentation
   702  // against third-party services.
   703  // An error will be returned if route does not have methods.
   704  func (r *Route) GetMethods() ([]string, error) {
   705  	if r.err != nil {
   706  		return nil, r.err
   707  	}
   708  	for _, m := range r.matchers {
   709  		if methods, ok := m.(methodMatcher); ok {
   710  			return []string(methods), nil
   711  		}
   712  	}
   713  	return nil, errors.New("mux: route doesn't have methods")
   714  }
   715  
   716  // GetHostTemplate returns the template used to build the
   717  // route match.
   718  // This is useful for building simple REST API documentation and for instrumentation
   719  // against third-party services.
   720  // An error will be returned if the route does not define a host.
   721  func (r *Route) GetHostTemplate() (string, error) {
   722  	if r.err != nil {
   723  		return "", r.err
   724  	}
   725  	if r.regexp.host == nil {
   726  		return "", errors.New("mux: route doesn't have a host")
   727  	}
   728  	return r.regexp.host.template, nil
   729  }
   730  
   731  // GetVarNames returns the names of all variables added by regexp matchers
   732  // These can be used to know which route variables should be passed into r.URL()
   733  func (r *Route) GetVarNames() ([]string, error) {
   734  	if r.err != nil {
   735  		return nil, r.err
   736  	}
   737  	var varNames []string
   738  	if r.regexp.host != nil {
   739  		varNames = append(varNames, r.regexp.host.varsN...)
   740  	}
   741  	if r.regexp.path != nil {
   742  		varNames = append(varNames, r.regexp.path.varsN...)
   743  	}
   744  	for _, regx := range r.regexp.queries {
   745  		varNames = append(varNames, regx.varsN...)
   746  	}
   747  	return varNames, nil
   748  }
   749  
   750  // prepareVars converts the route variable pairs into a map. If the route has a
   751  // BuildVarsFunc, it is invoked.
   752  func (r *Route) prepareVars(pairs ...string) (map[string]string, error) {
   753  	m, err := mapFromPairsToString(pairs...)
   754  	if err != nil {
   755  		return nil, err
   756  	}
   757  	return r.buildVars(m), nil
   758  }
   759  
   760  func (r *Route) buildVars(m map[string]string) map[string]string {
   761  	if r.buildVarsFunc != nil {
   762  		m = r.buildVarsFunc(m)
   763  	}
   764  	return m
   765  }
   766  

View as plain text