mirror of https://github.com/gorilla/mux
Mirror of https://github.com/gorilla/mux
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
746 lines
22 KiB
746 lines
22 KiB
// Copyright 2012 The Gorilla Authors. All rights reserved. |
|
// Use of this source code is governed by a BSD-style |
|
// license that can be found in the LICENSE file. |
|
|
|
package mux |
|
|
|
import ( |
|
"errors" |
|
"fmt" |
|
"net/http" |
|
"net/url" |
|
"regexp" |
|
"strings" |
|
) |
|
|
|
// Route stores information to match a request and build URLs. |
|
type Route struct { |
|
// Request handler for the route. |
|
handler http.Handler |
|
// If true, this route never matches: it is only used to build URLs. |
|
buildOnly bool |
|
// The name used to build URLs. |
|
name string |
|
// Error resulted from building a route. |
|
err error |
|
|
|
// "global" reference to all named routes |
|
namedRoutes map[string]*Route |
|
|
|
// config possibly passed in from `Router` |
|
routeConf |
|
} |
|
|
|
// SkipClean reports whether path cleaning is enabled for this route via |
|
// Router.SkipClean. |
|
func (r *Route) SkipClean() bool { |
|
return r.skipClean |
|
} |
|
|
|
// Match matches the route against the request. |
|
func (r *Route) Match(req *http.Request, match *RouteMatch) bool { |
|
if r.buildOnly || r.err != nil { |
|
return false |
|
} |
|
|
|
var matchErr error |
|
|
|
// Match everything. |
|
for _, m := range r.matchers { |
|
if matched := m.Match(req, match); !matched { |
|
if _, ok := m.(methodMatcher); ok { |
|
matchErr = ErrMethodMismatch |
|
continue |
|
} |
|
|
|
// Ignore ErrNotFound errors. These errors arise from match call |
|
// to Subrouters. |
|
// |
|
// This prevents subsequent matching subrouters from failing to |
|
// run middleware. If not ignored, the middleware would see a |
|
// non-nil MatchErr and be skipped, even when there was a |
|
// matching route. |
|
if match.MatchErr == ErrNotFound { |
|
match.MatchErr = nil |
|
} |
|
|
|
matchErr = nil // nolint:ineffassign |
|
return false |
|
} else { |
|
// Multiple routes may share the same path but use different HTTP methods. For instance: |
|
// Route 1: POST "/users/{id}". |
|
// Route 2: GET "/users/{id}", parameters: "id": "[0-9]+". |
|
// |
|
// The router must handle these cases correctly. For a GET request to "/users/abc" with "id" as "-2", |
|
// The router should return a "Not Found" error as no route fully matches this request. |
|
if match.MatchErr == ErrMethodMismatch { |
|
match.MatchErr = nil |
|
} |
|
} |
|
} |
|
|
|
if matchErr != nil { |
|
match.MatchErr = matchErr |
|
return false |
|
} |
|
|
|
if match.MatchErr == ErrMethodMismatch && r.handler != nil { |
|
// We found a route which matches request method, clear MatchErr |
|
match.MatchErr = nil |
|
// Then override the mis-matched handler |
|
match.Handler = r.handler |
|
} |
|
|
|
// Yay, we have a match. Let's collect some info about it. |
|
if match.Route == nil { |
|
match.Route = r |
|
} |
|
if match.Handler == nil { |
|
match.Handler = r.handler |
|
} |
|
if match.Vars == nil { |
|
match.Vars = make(map[string]string) |
|
} |
|
|
|
// Set variables. |
|
r.regexp.setMatch(req, match, r) |
|
return true |
|
} |
|
|
|
// ---------------------------------------------------------------------------- |
|
// Route attributes |
|
// ---------------------------------------------------------------------------- |
|
|
|
// GetError returns an error resulted from building the route, if any. |
|
func (r *Route) GetError() error { |
|
return r.err |
|
} |
|
|
|
// BuildOnly sets the route to never match: it is only used to build URLs. |
|
func (r *Route) BuildOnly() *Route { |
|
r.buildOnly = true |
|
return r |
|
} |
|
|
|
// Handler -------------------------------------------------------------------- |
|
|
|
// Handler sets a handler for the route. |
|
func (r *Route) Handler(handler http.Handler) *Route { |
|
if r.err == nil { |
|
r.handler = handler |
|
} |
|
return r |
|
} |
|
|
|
// HandlerFunc sets a handler function for the route. |
|
func (r *Route) HandlerFunc(f func(http.ResponseWriter, *http.Request)) *Route { |
|
return r.Handler(http.HandlerFunc(f)) |
|
} |
|
|
|
// GetHandler returns the handler for the route, if any. |
|
func (r *Route) GetHandler() http.Handler { |
|
return r.handler |
|
} |
|
|
|
// Name ----------------------------------------------------------------------- |
|
|
|
// Name sets the name for the route, used to build URLs. |
|
// It is an error to call Name more than once on a route. |
|
func (r *Route) Name(name string) *Route { |
|
if r.name != "" { |
|
r.err = fmt.Errorf("mux: route already has name %q, can't set %q", |
|
r.name, name) |
|
} |
|
if r.err == nil { |
|
r.name = name |
|
r.namedRoutes[name] = r |
|
} |
|
return r |
|
} |
|
|
|
// GetName returns the name for the route, if any. |
|
func (r *Route) GetName() string { |
|
return r.name |
|
} |
|
|
|
// ---------------------------------------------------------------------------- |
|
// Matchers |
|
// ---------------------------------------------------------------------------- |
|
|
|
// matcher types try to match a request. |
|
type matcher interface { |
|
Match(*http.Request, *RouteMatch) bool |
|
} |
|
|
|
// addMatcher adds a matcher to the route. |
|
func (r *Route) addMatcher(m matcher) *Route { |
|
if r.err == nil { |
|
r.matchers = append(r.matchers, m) |
|
} |
|
return r |
|
} |
|
|
|
// addRegexpMatcher adds a host or path matcher and builder to a route. |
|
func (r *Route) addRegexpMatcher(tpl string, typ regexpType) error { |
|
if r.err != nil { |
|
return r.err |
|
} |
|
if typ == regexpTypePath || typ == regexpTypePrefix { |
|
if len(tpl) > 0 && tpl[0] != '/' { |
|
return fmt.Errorf("mux: path must start with a slash, got %q", tpl) |
|
} |
|
if r.regexp.path != nil { |
|
tpl = strings.TrimRight(r.regexp.path.template, "/") + tpl |
|
} |
|
} |
|
rr, err := newRouteRegexp(tpl, typ, routeRegexpOptions{ |
|
strictSlash: r.strictSlash, |
|
useEncodedPath: r.useEncodedPath, |
|
}) |
|
if err != nil { |
|
return err |
|
} |
|
for _, q := range r.regexp.queries { |
|
if err = uniqueVars(rr.varsN, q.varsN); err != nil { |
|
return err |
|
} |
|
} |
|
if typ == regexpTypeHost { |
|
if r.regexp.path != nil { |
|
if err = uniqueVars(rr.varsN, r.regexp.path.varsN); err != nil { |
|
return err |
|
} |
|
} |
|
r.regexp.host = rr |
|
} else { |
|
if r.regexp.host != nil { |
|
if err = uniqueVars(rr.varsN, r.regexp.host.varsN); err != nil { |
|
return err |
|
} |
|
} |
|
if typ == regexpTypeQuery { |
|
r.regexp.queries = append(r.regexp.queries, rr) |
|
} else { |
|
r.regexp.path = rr |
|
} |
|
} |
|
r.addMatcher(rr) |
|
return nil |
|
} |
|
|
|
// Headers -------------------------------------------------------------------- |
|
|
|
// headerMatcher matches the request against header values. |
|
type headerMatcher map[string]string |
|
|
|
func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool { |
|
return matchMapWithString(m, r.Header, true) |
|
} |
|
|
|
// Headers adds a matcher for request header values. |
|
// It accepts a sequence of key/value pairs to be matched. For example: |
|
// |
|
// r := mux.NewRouter().NewRoute() |
|
// r.Headers("Content-Type", "application/json", |
|
// "X-Requested-With", "XMLHttpRequest") |
|
// |
|
// The above route will only match if both request header values match. |
|
// If the value is an empty string, it will match any value if the key is set. |
|
func (r *Route) Headers(pairs ...string) *Route { |
|
if r.err == nil { |
|
var headers map[string]string |
|
headers, r.err = mapFromPairsToString(pairs...) |
|
return r.addMatcher(headerMatcher(headers)) |
|
} |
|
return r |
|
} |
|
|
|
// headerRegexMatcher matches the request against the route given a regex for the header |
|
type headerRegexMatcher map[string]*regexp.Regexp |
|
|
|
func (m headerRegexMatcher) Match(r *http.Request, match *RouteMatch) bool { |
|
return matchMapWithRegex(m, r.Header, true) |
|
} |
|
|
|
// HeadersRegexp accepts a sequence of key/value pairs, where the value has regex |
|
// support. For example: |
|
// |
|
// r := mux.NewRouter().NewRoute() |
|
// r.HeadersRegexp("Content-Type", "application/(text|json)", |
|
// "X-Requested-With", "XMLHttpRequest") |
|
// |
|
// The above route will only match if both the request header matches both regular expressions. |
|
// If the value is an empty string, it will match any value if the key is set. |
|
// Use the start and end of string anchors (^ and $) to match an exact value. |
|
func (r *Route) HeadersRegexp(pairs ...string) *Route { |
|
if r.err == nil { |
|
var headers map[string]*regexp.Regexp |
|
headers, r.err = mapFromPairsToRegex(pairs...) |
|
return r.addMatcher(headerRegexMatcher(headers)) |
|
} |
|
return r |
|
} |
|
|
|
// Host ----------------------------------------------------------------------- |
|
|
|
// Host adds a matcher for the URL host. |
|
// It accepts a template with zero or more URL variables enclosed by {}. |
|
// Variables can define an optional regexp pattern to be matched: |
|
// |
|
// - {name} matches anything until the next dot. |
|
// |
|
// - {name:pattern} matches the given regexp pattern. |
|
// |
|
// For example: |
|
// |
|
// r := mux.NewRouter().NewRoute() |
|
// r.Host("www.example.com") |
|
// r.Host("{subdomain}.domain.com") |
|
// r.Host("{subdomain:[a-z]+}.domain.com") |
|
// |
|
// Variable names must be unique in a given route. They can be retrieved |
|
// calling mux.Vars(request). |
|
func (r *Route) Host(tpl string) *Route { |
|
r.err = r.addRegexpMatcher(tpl, regexpTypeHost) |
|
return r |
|
} |
|
|
|
// MatcherFunc ---------------------------------------------------------------- |
|
|
|
// MatcherFunc is the function signature used by custom matchers. |
|
type MatcherFunc func(*http.Request, *RouteMatch) bool |
|
|
|
// Match returns the match for a given request. |
|
func (m MatcherFunc) Match(r *http.Request, match *RouteMatch) bool { |
|
return m(r, match) |
|
} |
|
|
|
// MatcherFunc adds a custom function to be used as request matcher. |
|
func (r *Route) MatcherFunc(f MatcherFunc) *Route { |
|
return r.addMatcher(f) |
|
} |
|
|
|
// Methods -------------------------------------------------------------------- |
|
|
|
// methodMatcher matches the request against HTTP methods. |
|
type methodMatcher []string |
|
|
|
func (m methodMatcher) Match(r *http.Request, match *RouteMatch) bool { |
|
return matchInArray(m, r.Method) |
|
} |
|
|
|
// Methods adds a matcher for HTTP methods. |
|
// It accepts a sequence of one or more methods to be matched, e.g.: |
|
// "GET", "POST", "PUT". |
|
func (r *Route) Methods(methods ...string) *Route { |
|
for k, v := range methods { |
|
methods[k] = strings.ToUpper(v) |
|
} |
|
return r.addMatcher(methodMatcher(methods)) |
|
} |
|
|
|
// Path ----------------------------------------------------------------------- |
|
|
|
// Path adds a matcher for the URL path. |
|
// It accepts a template with zero or more URL variables enclosed by {}. The |
|
// template must start with a "/". |
|
// Variables can define an optional regexp pattern to be matched: |
|
// |
|
// - {name} matches anything until the next slash. |
|
// |
|
// - {name:pattern} matches the given regexp pattern. |
|
// |
|
// For example: |
|
// |
|
// r := mux.NewRouter().NewRoute() |
|
// r.Path("/products/").Handler(ProductsHandler) |
|
// r.Path("/products/{key}").Handler(ProductsHandler) |
|
// r.Path("/articles/{category}/{id:[0-9]+}"). |
|
// Handler(ArticleHandler) |
|
// |
|
// Variable names must be unique in a given route. They can be retrieved |
|
// calling mux.Vars(request). |
|
func (r *Route) Path(tpl string) *Route { |
|
r.err = r.addRegexpMatcher(tpl, regexpTypePath) |
|
return r |
|
} |
|
|
|
// PathPrefix ----------------------------------------------------------------- |
|
|
|
// PathPrefix adds a matcher for the URL path prefix. This matches if the given |
|
// template is a prefix of the full URL path. See Route.Path() for details on |
|
// the tpl argument. |
|
// |
|
// Note that it does not treat slashes specially ("/foobar/" will be matched by |
|
// the prefix "/foo") so you may want to use a trailing slash here. |
|
// |
|
// Also note that the setting of Router.StrictSlash() has no effect on routes |
|
// with a PathPrefix matcher. |
|
func (r *Route) PathPrefix(tpl string) *Route { |
|
r.err = r.addRegexpMatcher(tpl, regexpTypePrefix) |
|
return r |
|
} |
|
|
|
// Query ---------------------------------------------------------------------- |
|
|
|
// Queries adds a matcher for URL query values. |
|
// It accepts a sequence of key/value pairs. Values may define variables. |
|
// For example: |
|
// |
|
// r := mux.NewRouter().NewRoute() |
|
// r.Queries("foo", "bar", "id", "{id:[0-9]+}") |
|
// |
|
// The above route will only match if the URL contains the defined queries |
|
// values, e.g.: ?foo=bar&id=42. |
|
// |
|
// If the value is an empty string, it will match any value if the key is set. |
|
// |
|
// Variables can define an optional regexp pattern to be matched: |
|
// |
|
// - {name} matches anything until the next slash. |
|
// |
|
// - {name:pattern} matches the given regexp pattern. |
|
func (r *Route) Queries(pairs ...string) *Route { |
|
length := len(pairs) |
|
if length%2 != 0 { |
|
r.err = fmt.Errorf( |
|
"mux: number of parameters must be multiple of 2, got %v", pairs) |
|
return nil |
|
} |
|
for i := 0; i < length; i += 2 { |
|
if r.err = r.addRegexpMatcher(pairs[i]+"="+pairs[i+1], regexpTypeQuery); r.err != nil { |
|
return r |
|
} |
|
} |
|
|
|
return r |
|
} |
|
|
|
// Schemes -------------------------------------------------------------------- |
|
|
|
// schemeMatcher matches the request against URL schemes. |
|
type schemeMatcher []string |
|
|
|
func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool { |
|
scheme := r.URL.Scheme |
|
// https://golang.org/pkg/net/http/#Request |
|
// "For [most] server requests, fields other than Path and RawQuery will be |
|
// empty." |
|
// Since we're an http muxer, the scheme is either going to be http or https |
|
// though, so we can just set it based on the tls termination state. |
|
if scheme == "" { |
|
if r.TLS == nil { |
|
scheme = "http" |
|
} else { |
|
scheme = "https" |
|
} |
|
} |
|
return matchInArray(m, scheme) |
|
} |
|
|
|
// Schemes adds a matcher for URL schemes. |
|
// It accepts a sequence of schemes to be matched, e.g.: "http", "https". |
|
// If the request's URL has a scheme set, it will be matched against. |
|
// Generally, the URL scheme will only be set if a previous handler set it, |
|
// such as the ProxyHeaders handler from gorilla/handlers. |
|
// If unset, the scheme will be determined based on the request's TLS |
|
// termination state. |
|
// The first argument to Schemes will be used when constructing a route URL. |
|
func (r *Route) Schemes(schemes ...string) *Route { |
|
for k, v := range schemes { |
|
schemes[k] = strings.ToLower(v) |
|
} |
|
if len(schemes) > 0 { |
|
r.buildScheme = schemes[0] |
|
} |
|
return r.addMatcher(schemeMatcher(schemes)) |
|
} |
|
|
|
// BuildVarsFunc -------------------------------------------------------------- |
|
|
|
// BuildVarsFunc is the function signature used by custom build variable |
|
// functions (which can modify route variables before a route's URL is built). |
|
type BuildVarsFunc func(map[string]string) map[string]string |
|
|
|
// BuildVarsFunc adds a custom function to be used to modify build variables |
|
// before a route's URL is built. |
|
func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route { |
|
if r.buildVarsFunc != nil { |
|
// compose the old and new functions |
|
old := r.buildVarsFunc |
|
r.buildVarsFunc = func(m map[string]string) map[string]string { |
|
return f(old(m)) |
|
} |
|
} else { |
|
r.buildVarsFunc = f |
|
} |
|
return r |
|
} |
|
|
|
// Subrouter ------------------------------------------------------------------ |
|
|
|
// Subrouter creates a subrouter for the route. |
|
// |
|
// It will test the inner routes only if the parent route matched. For example: |
|
// |
|
// r := mux.NewRouter().NewRoute() |
|
// s := r.Host("www.example.com").Subrouter() |
|
// s.HandleFunc("/products/", ProductsHandler) |
|
// s.HandleFunc("/products/{key}", ProductHandler) |
|
// s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler) |
|
// |
|
// Here, the routes registered in the subrouter won't be tested if the host |
|
// doesn't match. |
|
func (r *Route) Subrouter() *Router { |
|
// initialize a subrouter with a copy of the parent route's configuration |
|
router := &Router{routeConf: copyRouteConf(r.routeConf), namedRoutes: r.namedRoutes} |
|
r.addMatcher(router) |
|
return router |
|
} |
|
|
|
// ---------------------------------------------------------------------------- |
|
// URL building |
|
// ---------------------------------------------------------------------------- |
|
|
|
// URL builds a URL for the route. |
|
// |
|
// It accepts a sequence of key/value pairs for the route variables. For |
|
// example, given this route: |
|
// |
|
// r := mux.NewRouter() |
|
// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). |
|
// Name("article") |
|
// |
|
// ...a URL for it can be built using: |
|
// |
|
// url, err := r.Get("article").URL("category", "technology", "id", "42") |
|
// |
|
// ...which will return an url.URL with the following path: |
|
// |
|
// "/articles/technology/42" |
|
// |
|
// This also works for host variables: |
|
// |
|
// r := mux.NewRouter() |
|
// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). |
|
// Host("{subdomain}.domain.com"). |
|
// Name("article") |
|
// |
|
// // url.String() will be "http://news.domain.com/articles/technology/42" |
|
// url, err := r.Get("article").URL("subdomain", "news", |
|
// "category", "technology", |
|
// "id", "42") |
|
// |
|
// The scheme of the resulting url will be the first argument that was passed to Schemes: |
|
// |
|
// // url.String() will be "https://example.com" |
|
// r := mux.NewRouter().NewRoute() |
|
// url, err := r.Host("example.com") |
|
// .Schemes("https", "http").URL() |
|
// |
|
// All variables defined in the route are required, and their values must |
|
// conform to the corresponding patterns. |
|
func (r *Route) URL(pairs ...string) (*url.URL, error) { |
|
if r.err != nil { |
|
return nil, r.err |
|
} |
|
values, err := r.prepareVars(pairs...) |
|
if err != nil { |
|
return nil, err |
|
} |
|
var scheme, host, path string |
|
queries := make([]string, 0, len(r.regexp.queries)) |
|
if r.regexp.host != nil { |
|
if host, err = r.regexp.host.url(values); err != nil { |
|
return nil, err |
|
} |
|
scheme = "http" |
|
if r.buildScheme != "" { |
|
scheme = r.buildScheme |
|
} |
|
} |
|
if r.regexp.path != nil { |
|
if path, err = r.regexp.path.url(values); err != nil { |
|
return nil, err |
|
} |
|
} |
|
for _, q := range r.regexp.queries { |
|
var query string |
|
if query, err = q.url(values); err != nil { |
|
return nil, err |
|
} |
|
queries = append(queries, query) |
|
} |
|
return &url.URL{ |
|
Scheme: scheme, |
|
Host: host, |
|
Path: path, |
|
RawQuery: strings.Join(queries, "&"), |
|
}, nil |
|
} |
|
|
|
// URLHost builds the host part of the URL for a route. See Route.URL(). |
|
// |
|
// The route must have a host defined. |
|
func (r *Route) URLHost(pairs ...string) (*url.URL, error) { |
|
if r.err != nil { |
|
return nil, r.err |
|
} |
|
if r.regexp.host == nil { |
|
return nil, errors.New("mux: route doesn't have a host") |
|
} |
|
values, err := r.prepareVars(pairs...) |
|
if err != nil { |
|
return nil, err |
|
} |
|
host, err := r.regexp.host.url(values) |
|
if err != nil { |
|
return nil, err |
|
} |
|
u := &url.URL{ |
|
Scheme: "http", |
|
Host: host, |
|
} |
|
if r.buildScheme != "" { |
|
u.Scheme = r.buildScheme |
|
} |
|
return u, nil |
|
} |
|
|
|
// URLPath builds the path part of the URL for a route. See Route.URL(). |
|
// |
|
// The route must have a path defined. |
|
func (r *Route) URLPath(pairs ...string) (*url.URL, error) { |
|
if r.err != nil { |
|
return nil, r.err |
|
} |
|
if r.regexp.path == nil { |
|
return nil, errors.New("mux: route doesn't have a path") |
|
} |
|
values, err := r.prepareVars(pairs...) |
|
if err != nil { |
|
return nil, err |
|
} |
|
path, err := r.regexp.path.url(values) |
|
if err != nil { |
|
return nil, err |
|
} |
|
return &url.URL{ |
|
Path: path, |
|
}, nil |
|
} |
|
|
|
// GetPathTemplate returns the template used to build the |
|
// route match. |
|
// This is useful for building simple REST API documentation and for instrumentation |
|
// against third-party services. |
|
// An error will be returned if the route does not define a path. |
|
func (r *Route) GetPathTemplate() (string, error) { |
|
if r.err != nil { |
|
return "", r.err |
|
} |
|
if r.regexp.path == nil { |
|
return "", errors.New("mux: route doesn't have a path") |
|
} |
|
return r.regexp.path.template, nil |
|
} |
|
|
|
// GetPathRegexp returns the expanded regular expression used to match route path. |
|
// This is useful for building simple REST API documentation and for instrumentation |
|
// against third-party services. |
|
// An error will be returned if the route does not define a path. |
|
func (r *Route) GetPathRegexp() (string, error) { |
|
if r.err != nil { |
|
return "", r.err |
|
} |
|
if r.regexp.path == nil { |
|
return "", errors.New("mux: route does not have a path") |
|
} |
|
return r.regexp.path.regexp.String(), nil |
|
} |
|
|
|
// GetQueriesRegexp returns the expanded regular expressions used to match the |
|
// route queries. |
|
// This is useful for building simple REST API documentation and for instrumentation |
|
// against third-party services. |
|
// An error will be returned if the route does not have queries. |
|
func (r *Route) GetQueriesRegexp() ([]string, error) { |
|
if r.err != nil { |
|
return nil, r.err |
|
} |
|
if r.regexp.queries == nil { |
|
return nil, errors.New("mux: route doesn't have queries") |
|
} |
|
queries := make([]string, 0, len(r.regexp.queries)) |
|
for _, query := range r.regexp.queries { |
|
queries = append(queries, query.regexp.String()) |
|
} |
|
return queries, nil |
|
} |
|
|
|
// GetQueriesTemplates returns the templates used to build the |
|
// query matching. |
|
// This is useful for building simple REST API documentation and for instrumentation |
|
// against third-party services. |
|
// An error will be returned if the route does not define queries. |
|
func (r *Route) GetQueriesTemplates() ([]string, error) { |
|
if r.err != nil { |
|
return nil, r.err |
|
} |
|
if r.regexp.queries == nil { |
|
return nil, errors.New("mux: route doesn't have queries") |
|
} |
|
queries := make([]string, 0, len(r.regexp.queries)) |
|
for _, query := range r.regexp.queries { |
|
queries = append(queries, query.template) |
|
} |
|
return queries, nil |
|
} |
|
|
|
// GetMethods returns the methods the route matches against |
|
// This is useful for building simple REST API documentation and for instrumentation |
|
// against third-party services. |
|
// An error will be returned if route does not have methods. |
|
func (r *Route) GetMethods() ([]string, error) { |
|
if r.err != nil { |
|
return nil, r.err |
|
} |
|
for _, m := range r.matchers { |
|
if methods, ok := m.(methodMatcher); ok { |
|
return []string(methods), nil |
|
} |
|
} |
|
return nil, errors.New("mux: route doesn't have methods") |
|
} |
|
|
|
// GetHostTemplate returns the template used to build the |
|
// route match. |
|
// This is useful for building simple REST API documentation and for instrumentation |
|
// against third-party services. |
|
// An error will be returned if the route does not define a host. |
|
func (r *Route) GetHostTemplate() (string, error) { |
|
if r.err != nil { |
|
return "", r.err |
|
} |
|
if r.regexp.host == nil { |
|
return "", errors.New("mux: route doesn't have a host") |
|
} |
|
return r.regexp.host.template, nil |
|
} |
|
|
|
// prepareVars converts the route variable pairs into a map. If the route has a |
|
// BuildVarsFunc, it is invoked. |
|
func (r *Route) prepareVars(pairs ...string) (map[string]string, error) { |
|
m, err := mapFromPairsToString(pairs...) |
|
if err != nil { |
|
return nil, err |
|
} |
|
return r.buildVars(m), nil |
|
} |
|
|
|
func (r *Route) buildVars(m map[string]string) map[string]string { |
|
if r.buildVarsFunc != nil { |
|
m = r.buildVarsFunc(m) |
|
} |
|
return m |
|
}
|
|
|