本站源代码
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.

764 lines
22KB

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