1 // Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
2 // Revel Framework source code and usage is governed by a MIT style
3 // license that can be found in the LICENSE file.
20 "github.com/revel/pathtree"
21 "github.com/revel/revel/logger"
25 httpStatusCode = "404"
29 ModuleSource *Module // Module name of route
30 Method string // e.g. GET
31 Path string // e.g. /app/:id
32 Action string // e.g. "Application.ShowApp", "404"
33 ControllerNamespace string // e.g. "testmodule.",
34 ControllerName string // e.g. "Application", ""
35 MethodName string // e.g. "ShowApp", ""
36 FixedParams []string // e.g. "arg1","arg2","arg3" (CSV formatting)
37 TreePath string // e.g. "/GET/app/:id"
38 TypeOfController *ControllerType // The controller type (if route is not wild carded)
40 routesPath string // e.g. /Users/robfig/gocode/src/myapp/conf/routes
44 type RouteMatch struct {
45 Action string // e.g. 404
46 ControllerName string // e.g. Application
47 MethodName string // e.g. ShowApp
49 Params map[string][]string // e.g. {id: 123}
50 TypeOfController *ControllerType // The controller type
51 ModuleSource *Module // The module
54 type ActionPathData struct {
55 Key string // The unique key
56 ControllerNamespace string // The controller namespace
57 ControllerName string // The controller name
58 MethodName string // The method name
59 Action string // The action
60 ModuleSource *Module // The module
61 Route *Route // The route
62 FixedParamsByName map[string]string // The fixed parameters
63 TypeOfController *ControllerType // The controller type
67 // Used to store decoded action path mappings
68 actionPathCacheMap = map[string]*ActionPathData{}
69 // Used to prevent concurrent writes to map
70 actionPathCacheLock = sync.Mutex{}
71 // The path returned if not found
72 notFound = &RouteMatch{Action: "404"}
75 var routerLog = RevelLog.New("section", "router")
78 AddInitEventHandler(func(typeOf Event, value interface{}) (responseOf EventResponse) {
80 if typeOf == ROUTE_REFRESH_REQUESTED {
81 // Clear the actionPathCacheMap cache
82 actionPathCacheLock.Lock()
83 defer actionPathCacheLock.Unlock()
84 actionPathCacheMap = map[string]*ActionPathData{}
90 // NewRoute prepares the route to be used in matching.
91 func NewRoute(moduleSource *Module, method, path, action, fixedArgs, routesPath string, line int) (r *Route) {
92 // Handle fixed arguments
93 argsReader := strings.NewReader(string(namespaceReplace([]byte(fixedArgs), moduleSource)))
94 csvReader := csv.NewReader(argsReader)
95 csvReader.TrimLeadingSpace = true
96 fargs, err := csvReader.Read()
97 if err != nil && err != io.EOF {
98 routerLog.Error("NewRoute: Invalid fixed parameters for string ", "error", err, "fixedargs", fixedArgs)
102 ModuleSource: moduleSource,
103 Method: strings.ToUpper(method),
105 Action: string(namespaceReplace([]byte(action), moduleSource)),
107 TreePath: treePath(strings.ToUpper(method), path),
108 routesPath: routesPath,
113 if !strings.HasPrefix(r.Path, "/") {
114 routerLog.Error("NewRoute: Absolute URL required.")
118 // Ignore the not found status code
119 if action != httpStatusCode {
120 routerLog.Debugf("NewRoute: New splitActionPath path:%s action:%s", path, action)
121 pathData, found := splitActionPath(&ActionPathData{ModuleSource: moduleSource, Route: r}, r.Action, false)
123 if pathData.TypeOfController != nil {
124 // Assign controller type to avoid looking it up based on name
125 r.TypeOfController = pathData.TypeOfController
126 // Create the fixed parameters
127 if l := len(pathData.Route.FixedParams); l > 0 && len(pathData.FixedParamsByName) == 0 {
128 methodType := pathData.TypeOfController.Method(pathData.MethodName)
129 if methodType != nil {
130 pathData.FixedParamsByName = make(map[string]string, l)
131 for i, argValue := range pathData.Route.FixedParams {
132 Unbind(pathData.FixedParamsByName, methodType.Args[i].Name, argValue)
135 routerLog.Panicf("NewRoute: Method %s not found for controller %s", pathData.MethodName, pathData.ControllerName)
139 r.ControllerNamespace = pathData.ControllerNamespace
140 r.ControllerName = pathData.ControllerName
141 r.ModuleSource = pathData.ModuleSource
142 r.MethodName = pathData.MethodName
144 // The same action path could be used for multiple routes (like the Static.Serve)
146 routerLog.Panicf("NewRoute: Failed to find controller for route path action %s \n%#v\n", path+"?"+r.Action, actionPathCacheMap)
152 func (route *Route) ActionPath() string {
153 return route.ModuleSource.Namespace() + route.ControllerName
156 func treePath(method, path string) string {
160 return "/" + method + path
166 Module string // The module the route is associated with
167 path string // path to the routes file
170 func (router *Router) Route(req *Request) (routeMatch *RouteMatch) {
171 // Override method if set in header
172 if method := req.GetHttpHeader("X-HTTP-Method-Override"); method != "" && req.Method == "POST" {
176 leaf, expansions := router.Tree.Find(treePath(req.Method, req.GetPath()))
181 // Create a map of the route parameters.
182 var params url.Values
183 if len(expansions) > 0 {
184 params = make(url.Values)
185 for i, v := range expansions {
186 params[leaf.Wildcards[i]] = []string{v}
190 var controllerName, methodName string
192 // The leaf value is now a list of possible routes to match, only a controller
193 routeList := leaf.Value.([]*Route)
194 var typeOfController *ControllerType
196 //INFO.Printf("Found route for path %s %#v", req.URL.Path, len(routeList))
197 for index := range routeList {
198 route = routeList[index]
199 methodName = route.MethodName
201 // Special handling for explicit 404's.
202 if route.Action == httpStatusCode {
207 // If wildcard match on method name use the method name from the params
208 if methodName[0] == ':' {
209 if methodKey, found := params[methodName[1:]]; found && len(methodKey) > 0 {
210 methodName = strings.ToLower(methodKey[0])
212 routerLog.Fatal("Route: Failure to find method name in parameters", "params", params, "methodName", methodName)
216 // If the action is variablized, replace into it with the captured args.
217 controllerName = route.ControllerName
218 if controllerName[0] == ':' {
219 controllerName = strings.ToLower(params[controllerName[1:]][0])
220 if typeOfController = route.ModuleSource.ControllerByName(controllerName, methodName); typeOfController != nil {
224 typeOfController = route.TypeOfController
231 routeMatch = notFound
234 routeMatch = &RouteMatch{
235 ControllerName: route.ControllerNamespace + controllerName,
236 MethodName: methodName,
238 FixedParams: route.FixedParams,
239 TypeOfController: typeOfController,
240 ModuleSource: route.ModuleSource,
247 // Refresh re-reads the routes file and re-calculates the routing table.
248 // Returns an error if a specified action could not be found.
249 func (router *Router) Refresh() (err *Error) {
250 RaiseEvent(ROUTE_REFRESH_REQUESTED, nil)
251 router.Routes, err = parseRoutesFile(appModule, router.path, "", true)
252 RaiseEvent(ROUTE_REFRESH_COMPLETED, nil)
256 err = router.updateTree()
260 func (router *Router) updateTree() *Error {
261 router.Tree = pathtree.New()
262 pathMap := map[string][]*Route{}
264 allPathsOrdered := []string{}
265 // It is possible for some route paths to overlap
266 // based on wildcard matches,
267 // TODO when pathtree is fixed (made to be smart enough to not require a predefined intake order) keeping the routes in order is not necessary
268 for _, route := range router.Routes {
269 if _, found := pathMap[route.TreePath]; !found {
270 pathMap[route.TreePath] = append(pathMap[route.TreePath], route)
271 allPathsOrdered = append(allPathsOrdered, route.TreePath)
273 pathMap[route.TreePath] = append(pathMap[route.TreePath], route)
276 for _, path := range allPathsOrdered {
277 routeList := pathMap[path]
278 err := router.Tree.Add(path, routeList)
280 // Allow GETs to respond to HEAD requests.
281 if err == nil && routeList[0].Method == "GET" {
282 err = router.Tree.Add(treePath("HEAD", routeList[0].Path), routeList)
285 // Error adding a route to the pathtree.
287 return routeError(err, path, fmt.Sprintf("%#v", routeList), routeList[0].line)
293 // Returns the controller namespace and name, action and module if found from the actionPath specified
294 func splitActionPath(actionPathData *ActionPathData, actionPath string, useCache bool) (pathData *ActionPathData, found bool) {
295 actionPath = strings.ToLower(actionPath)
296 if pathData, found = actionPathCacheMap[actionPath]; found && useCache {
300 controllerNamespace, controllerName, methodName, action string
301 foundModuleSource *Module
302 typeOfController *ControllerType
303 log = routerLog.New("actionPath", actionPath)
305 actionSplit := strings.Split(actionPath, ".")
306 if actionPathData != nil {
307 foundModuleSource = actionPathData.ModuleSource
309 if len(actionSplit) == 2 {
310 controllerName, methodName = strings.ToLower(actionSplit[0]), strings.ToLower(actionSplit[1])
311 if i := strings.Index(methodName, "("); i > 0 {
312 methodName = methodName[:i]
314 log = log.New("controller", controllerName, "method", methodName)
315 log.Debug("splitActionPath: Check for namespace")
316 if i := strings.Index(controllerName, namespaceSeperator); i > -1 {
317 controllerNamespace = controllerName[:i+1]
318 if moduleSource, found := ModuleByName(controllerNamespace[:len(controllerNamespace)-1]); found {
319 log.Debug("Found module namespace")
320 foundModuleSource = moduleSource
321 controllerNamespace = moduleSource.Namespace()
323 log.Warnf("splitActionPath: Unable to find module %s for action: %s", controllerNamespace[:len(controllerNamespace)-1], actionPath)
325 controllerName = controllerName[i+1:]
326 // Check for the type of controller
327 typeOfController = foundModuleSource.ControllerByName(controllerName, methodName)
328 found = typeOfController != nil
329 } else if controllerName[0] != ':' {
330 // First attempt to find the controller in the module source
331 if foundModuleSource != nil {
332 typeOfController = foundModuleSource.ControllerByName(controllerName, methodName)
333 if typeOfController != nil {
334 controllerNamespace = typeOfController.Namespace
337 log.Info("Found controller for path", "controllerType", typeOfController)
339 if typeOfController == nil {
340 // Check to see if we can determine the controller from only the controller name
341 // an actionPath without a moduleSource will only come from
342 // Scan through the controllers
343 matchName := controllerName
344 for key, controller := range controllers {
345 // Strip away the namespace from the controller. to be match
347 if i := strings.Index(key, namespaceSeperator); i > -1 {
348 regularName = regularName[i+1:]
350 if regularName == matchName {
351 // Found controller match
352 typeOfController = controller
353 controllerNamespace = typeOfController.Namespace
354 controllerName = typeOfController.ShortName()
355 foundModuleSource = typeOfController.ModuleSource
364 // If wildcard assign the route the controller namespace found
365 controllerNamespace = actionPathData.ModuleSource.Name + namespaceSeperator
366 foundModuleSource = actionPathData.ModuleSource
369 action = actionSplit[1]
372 for path := range actionPathCacheMap {
373 foundPaths += path + ","
375 log.Warnf("splitActionPath: Invalid action path %s found paths %s", actionPath, foundPaths)
379 // Make sure no concurrent map writes occur
381 actionPathCacheLock.Lock()
382 defer actionPathCacheLock.Unlock()
383 if actionPathData != nil {
384 actionPathData.ControllerNamespace = controllerNamespace
385 actionPathData.ControllerName = controllerName
386 actionPathData.MethodName = methodName
387 actionPathData.Action = action
388 actionPathData.ModuleSource = foundModuleSource
389 actionPathData.TypeOfController = typeOfController
391 actionPathData = &ActionPathData{
392 ControllerNamespace: controllerNamespace,
393 ControllerName: controllerName,
394 MethodName: methodName,
396 ModuleSource: foundModuleSource,
397 TypeOfController: typeOfController,
400 actionPathData.TypeOfController = foundModuleSource.ControllerByName(controllerName, "")
401 if actionPathData.TypeOfController == nil && actionPathData.ControllerName[0] != ':' {
402 log.Warnf("splitActionPath: No controller found for %s %#v", foundModuleSource.Namespace()+controllerName, controllers)
405 pathData = actionPathData
406 if pathData.Route != nil && len(pathData.Route.FixedParams) > 0 {
407 // If there are fixed params on the route then add them to the path
408 // This will give it a unique path and it should still be usable for a reverse lookup provided the name is matchable
410 // GET /test/ Application.Index("Test", "Test2")
411 // {{url "Application.Index(test,test)" }}
412 // should be parseable
413 actionPath = actionPath + "(" + strings.ToLower(strings.Join(pathData.Route.FixedParams, ",")) + ")"
415 if actionPathData.Route != nil {
416 log.Debugf("splitActionPath: Split Storing recognized action path %s for route %#v ", actionPath, actionPathData.Route)
418 pathData.Key = actionPath
419 actionPathCacheMap[actionPath] = pathData
420 if !strings.Contains(actionPath, namespaceSeperator) && pathData.TypeOfController != nil {
421 actionPathCacheMap[strings.ToLower(pathData.TypeOfController.Namespace)+actionPath] = pathData
422 log.Debugf("splitActionPath: Split Storing recognized action path %s for route %#v ", strings.ToLower(pathData.TypeOfController.Namespace)+actionPath, actionPathData.Route)
428 // parseRoutesFile reads the given routes file and returns the contained routes.
429 func parseRoutesFile(moduleSource *Module, routesPath, joinedPath string, validate bool) ([]*Route, *Error) {
430 contentBytes, err := ioutil.ReadFile(routesPath)
433 Title: "Failed to load routes file",
434 Description: err.Error(),
437 return parseRoutes(moduleSource, routesPath, joinedPath, string(contentBytes), validate)
440 // parseRoutes reads the content of a routes file into the routing table.
441 func parseRoutes(moduleSource *Module, routesPath, joinedPath, content string, validate bool) ([]*Route, *Error) {
445 for n, line := range strings.Split(content, "\n") {
446 line = strings.TrimSpace(line)
447 if len(line) == 0 || line[0] == '#' {
451 const modulePrefix = "module:"
453 // Handle included routes from modules.
454 // e.g. "module:testrunner" imports all routes from that module.
455 if strings.HasPrefix(line, modulePrefix) {
456 moduleRoutes, err := getModuleRoutes(line[len(modulePrefix):], joinedPath, validate)
458 return nil, routeError(err, routesPath, content, n)
460 routes = append(routes, moduleRoutes...)
465 method, path, action, fixedArgs, found := parseRouteLine(line)
470 // this will avoid accidental double forward slashes in a route.
471 // this also avoids pathtree freaking out and causing a runtime panic
472 // because of the double slashes
473 if strings.HasSuffix(joinedPath, "/") && strings.HasPrefix(path, "/") {
474 joinedPath = joinedPath[0 : len(joinedPath)-1]
476 path = strings.Join([]string{AppRoot, joinedPath, path}, "")
478 // This will import the module routes under the path described in the
479 // routes file (joinedPath param). e.g. "* /jobs module:jobs" -> all
480 // routes' paths will have the path /jobs prepended to them.
481 // See #282 for more info
482 if method == "*" && strings.HasPrefix(action, modulePrefix) {
483 moduleRoutes, err := getModuleRoutes(action[len(modulePrefix):], path, validate)
485 return nil, routeError(err, routesPath, content, n)
487 routes = append(routes, moduleRoutes...)
491 route := NewRoute(moduleSource, method, path, action, fixedArgs, routesPath, n)
492 routes = append(routes, route)
495 if err := validateRoute(route); err != nil {
496 return nil, routeError(err, routesPath, content, n)
504 // validateRoute checks that every specified action exists.
505 func validateRoute(route *Route) error {
507 if route.Action == httpStatusCode {
511 // Skip variable routes.
512 if route.ControllerName[0] == ':' || route.MethodName[0] == ':' {
516 // Precheck to see if controller exists
517 if _, found := controllers[route.ControllerNamespace+route.ControllerName]; !found {
518 // Scan through controllers to find module
519 for _, c := range controllers {
520 controllerName := strings.ToLower(c.Type.Name())
521 if controllerName == route.ControllerName {
522 route.ControllerNamespace = c.ModuleSource.Name + namespaceSeperator
523 routerLog.Warn("validateRoute: Matched empty namespace route for %s to this namespace %s for the route %s", controllerName, c.ModuleSource.Name, route.Path)
528 // TODO need to check later
529 // does it do only validation or validation and instantiate the controller.
531 return c.SetTypeAction(route.ControllerNamespace+route.ControllerName, route.MethodName, route.TypeOfController)
534 // routeError adds context to a simple error message.
535 func routeError(err error, routesPath, content string, n int) *Error {
536 if revelError, ok := err.(*Error); ok {
539 // Load the route file content if necessary
541 if contentBytes, er := ioutil.ReadFile(routesPath); er != nil {
542 routerLog.Error("routeError: Failed to read route file ", "file", routesPath, "error", er)
544 content = string(contentBytes)
548 Title: "Route validation error",
549 Description: err.Error(),
552 SourceLines: strings.Split(content, "\n"),
553 Stack: fmt.Sprintf("%s", logger.NewCallStack()),
557 // getModuleRoutes loads the routes file for the given module and returns the
559 func getModuleRoutes(moduleName, joinedPath string, validate bool) (routes []*Route, err *Error) {
560 // Look up the module. It may be not found due to the common case of e.g. the
561 // testrunner module being active only in dev mode.
562 module, found := ModuleByName(moduleName)
564 routerLog.Debug("getModuleRoutes: Skipping routes for inactive module", "module", moduleName)
567 routePath := filepath.Join(module.Path, "conf", "routes")
568 if _, e := os.Stat(routePath); e == nil {
569 routes, err = parseRoutesFile(module, routePath, joinedPath, validate)
572 for _, route := range routes {
573 route.ModuleSource = module
585 var routePattern = regexp.MustCompile(
586 "(?i)^(GET|POST|PUT|DELETE|PATCH|OPTIONS|HEAD|WS|PROPFIND|MKCOL|COPY|MOVE|PROPPATCH|LOCK|UNLOCK|TRACE|PURGE|\\*)" +
587 "[(]?([^)]*)(\\))?[ \t]+" +
588 "(.*/[^ \t]*)[ \t]+([^ \t(]+)" +
589 `\(?([^)]*)\)?[ \t]*$`)
591 func parseRouteLine(line string) (method, path, action, fixedArgs string, found bool) {
592 matches := routePattern.FindStringSubmatch(line)
596 method, path, action, fixedArgs = matches[1], matches[4], matches[5], matches[6]
601 func NewRouter(routesPath string) *Router {
603 Tree: pathtree.New(),
608 type ActionDefinition struct {
609 Host, Method, URL, Action string
611 Args map[string]string
614 func (a *ActionDefinition) String() string {
618 func (router *Router) Reverse(action string, argValues map[string]string) (ad *ActionDefinition) {
619 log := routerLog.New("action", action)
620 pathData, found := splitActionPath(nil, action, true)
622 routerLog.Error("splitActionPath: Failed to find reverse route", "action", action, "arguments", argValues)
626 log.Debug("Checking for route", "pathdataRoute", pathData.Route)
627 if pathData.Route == nil {
628 var possibleRoute *Route
629 // If the route is nil then we need to go through the routes to find the first matching route
630 // from this controllers namespace, this is likely a wildcard route match
631 for _, route := range router.Routes {
632 // Skip routes that are not wild card or empty
633 if route.ControllerName == "" || route.MethodName == "" {
636 if route.ModuleSource == pathData.ModuleSource && route.ControllerName[0] == ':' {
637 // Wildcard match in same module space
638 pathData.Route = route
640 } else if route.ActionPath() == pathData.ModuleSource.Namespace()+pathData.ControllerName &&
641 (route.Method[0] == ':' || route.Method == pathData.MethodName) {
643 pathData.Route = route
645 } else if route.ControllerName == pathData.ControllerName &&
646 (route.Method[0] == ':' || route.Method == pathData.MethodName) {
647 // Controller name match
648 possibleRoute = route
651 if pathData.Route == nil && possibleRoute != nil {
652 pathData.Route = possibleRoute
653 routerLog.Warnf("Reverse: For a url reverse a match was based on %s matched path to route %#v ", action, possibleRoute)
655 if pathData.Route != nil {
656 routerLog.Debugf("Reverse: Reverse Storing recognized action path %s for route %#v\n", action, pathData.Route)
660 // Likely unknown route because of a wildcard, perform manual lookup
661 if pathData.Route != nil {
662 route := pathData.Route
664 // If the controller or method are wildcards we need to populate the argValues
665 controllerWildcard := route.ControllerName[0] == ':'
666 methodWildcard := route.MethodName[0] == ':'
668 // populate route arguments with the names
669 if controllerWildcard {
670 argValues[route.ControllerName[1:]] = pathData.ControllerName
673 argValues[route.MethodName[1:]] = pathData.MethodName
675 // In theory all routes should be defined and pre-populated, the route controllers may not be though
676 // with wildcard routes
677 if pathData.TypeOfController == nil {
678 if controllerWildcard || methodWildcard {
679 if controller := ControllerTypeByName(pathData.ControllerNamespace+pathData.ControllerName, route.ModuleSource); controller != nil {
680 // Wildcard match boundary
681 pathData.TypeOfController = controller
682 // See if the path exists in the module based
684 routerLog.Errorf("Reverse: Controller %s not found in reverse lookup", pathData.ControllerNamespace+pathData.ControllerName)
690 if pathData.TypeOfController == nil {
691 routerLog.Errorf("Reverse: Controller %s not found in reverse lookup", pathData.ControllerNamespace+pathData.ControllerName)
695 queryValues = make(url.Values)
696 pathElements = strings.Split(route.Path, "/")
698 for i, el := range pathElements {
699 if el == "" || (el[0] != ':' && el[0] != '*') {
702 val, ok := pathData.FixedParamsByName[el[1:]]
704 val, ok = argValues[el[1:]]
708 routerLog.Error("Reverse: reverse route missing route argument ", "argument", el[1:])
710 pathElements[i] = val
711 delete(argValues, el[1:])
715 // Add any args that were not inserted into the path into the query string.
716 for k, v := range argValues {
717 queryValues.Set(k, v)
720 // Calculate the final URL and Method
721 urlPath := strings.Join(pathElements, "/")
722 if len(queryValues) > 0 {
723 urlPath += "?" + queryValues.Encode()
726 method := route.Method
728 if route.Method == "*" {
733 //INFO.Printf("Reversing action %s to %s Using Route %#v",action,url,pathData.Route)
735 return &ActionDefinition{
745 routerLog.Error("Reverse: Failed to find controller for reverse route", "action", action, "arguments", argValues)
749 func RouterFilter(c *Controller, fc []Filter) {
750 // Figure out the Controller/Action
751 route := MainRouter.Route(c.Request)
753 c.Result = c.NotFound("No matching route found: " + c.Request.GetRequestURI())
757 // The route may want to explicitly return a 404.
758 if route.Action == httpStatusCode {
759 c.Result = c.NotFound("(intentionally)")
764 if err := c.SetTypeAction(route.ControllerName, route.MethodName, route.TypeOfController); err != nil {
765 c.Result = c.NotFound(err.Error())
769 // Add the route and fixed params to the Request Params.
770 c.Params.Route = route.Params
771 // Assign logger if from module
772 if c.Type.ModuleSource != nil && c.Type.ModuleSource != appModule {
773 c.Log = c.Type.ModuleSource.Log.New("ip", c.ClientIP,
774 "path", c.Request.URL.Path, "method", c.Request.Method)
777 // Add the fixed parameters mapped by name.
778 // TODO: Pre-calculate this mapping.
779 for i, value := range route.FixedParams {
780 if c.Params.Fixed == nil {
781 c.Params.Fixed = make(url.Values)
783 if i < len(c.MethodType.Args) {
784 arg := c.MethodType.Args[i]
785 c.Params.Fixed.Set(arg.Name, value)
787 routerLog.Warn("RouterFilter: Too many parameters to action", "action", route.Action, "value", value)
795 // HTTPMethodOverride overrides allowed http methods via form or browser param
796 func HTTPMethodOverride(c *Controller, fc []Filter) {
797 // An array of HTTP verbs allowed.
798 verbs := []string{"POST", "PUT", "PATCH", "DELETE"}
800 method := strings.ToUpper(c.Request.Method)
802 if method == "POST" {
804 if f, err := c.Request.GetForm(); err == nil {
805 param = strings.ToUpper(f.Get("_method"))
810 // Check if param is allowed
811 for _, verb := range verbs {
819 c.Request.Method = param
821 c.Response.Status = 405
822 c.Result = c.RenderError(&Error{
823 Title: "Method not allowed",
824 Description: "Method " + param + " is not allowed (valid: " + strings.Join(verbs, ", ") + ")",
832 fc[0](c, fc[1:]) // Execute the next filter stage.
837 MainRouter = NewRouter(filepath.Join(BasePath, "conf", "routes"))
838 err := MainRouter.Refresh()
839 if MainWatcher != nil && Config.BoolDefault("watch.routes", true) {
840 MainWatcher.Listen(MainRouter, MainRouter.path)
841 } else if err != nil {
842 // Not in dev mode and Route loading failed, we should crash.
843 routerLog.Panic("init: router initialize error", "error", err)