route.go 22 KB

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