Router
package revel
type Route struct {
Method string
Path string
Action string
ControllerName string
MethodName string
FixedParams []string
TreePath string
}
type RouteMatch struct {
Action string
ControllerName string
MethodName string
FixedParams []string
Params map[string][]string
}
func NewRoute(method, path, action, fixedArgs, routesPath string, line int) (r *Route) {
argsReader := strings.NewReader(fixedArgs)
csv := csv.NewReader(argsReader)
fargs, err := csv.Read()
if err != nil && err != io.EOF {
ERROR.Printf("Invalid fixed parameters (%v): for string '%v'", err.Error(), fixedArgs)
}
r = &Route{
Method: strings.ToUpper(method),
Path: path,
Action: action,
FixedParams: fargs,
TreePath: treePath(strings.ToUpper(method), path),
routesPath: routesPath,
line: line,
}
if !strings.HasPrefix(r.Path, "/") {
ERROR.Print("Absolute URL required.")
return
}
actionSplit := strings.Split(action, ".")
if len(actionSplit) == 2 {
r.ControllerName = actionSplit[0]
r.MethodName = actionSplit[1]
}
return
}
type Router struct {
Routes []*Route
Tree *pathtree.Node
}
func (router *Router) Route(req *http.Request) *RouteMatch {
leaf, expansions := router.Tree.Find(treePath(req.Method, req.URL.Path))
if leaf == nil {
return nil
}
route := leaf.Value.(*Route)
var params url.Values
if len(expansions) > 0 {
params = make(url.Values)
for i, v := range expansions {
params[leaf.Wildcards[i]] = []string{v}
}
}
if route.Action == "404" {
return notFound
}
controllerName, methodName := route.ControllerName, route.MethodName
if controllerName[0] == ':' {
controllerName = params[controllerName[1:]][0]
}
if methodName[0] == ':' {
methodName = params[methodName[1:]][0]
}
return &RouteMatch{
ControllerName: controllerName,
MethodName: methodName,
Params: params,
FixedParams: route.FixedParams,
}
}
func (router *Router) Refresh() (err *Error) {
router.Routes, err = parseRoutesFile(router.path, "", true)
if err != nil {
return
}
err = router.updateTree()
return
}
func NewRouter(routesPath string) *Router {
return &Router{
Tree: pathtree.New(),
path: routesPath,
}
}
type ActionDefinition struct {
Host, Method, Url, Action string
Star bool
Args map[string]string
}
func (a *ActionDefinition) String() string {
return a.Url
}
func (router *Router) Reverse(action string, argValues map[string]string) *ActionDefinition {
actionSplit := strings.Split(action, ".")
if len(actionSplit) != 2 {
ERROR.Print("revel/router: reverse router got invalid action ", action)
return nil
}
controllerName, methodName := actionSplit[0], actionSplit[1]
for _, route := range router.Routes {
if route.ControllerName == "" || route.MethodName == "" {
continue
}
controllerWildcard := route.ControllerName[0] == ':'
methodWildcard := route.MethodName[0] == ':'
if (!controllerWildcard && route.ControllerName != controllerName) ||
(!methodWildcard && route.MethodName != methodName) {
continue
}
if controllerWildcard {
argValues[route.ControllerName[1:]] = controllerName
}
if methodWildcard {
argValues[route.MethodName[1:]] = methodName
}
var (
queryValues = make(url.Values)
pathElements = strings.Split(route.Path, "/")
)
for i, el := range pathElements {
if el == "" || el[0] != ':' {
continue
}
val, ok := argValues[el[1:]]
if !ok {
val = "<nil>"
ERROR.Print("revel/router: reverse route missing route arg ", el[1:])
}
pathElements[i] = val
delete(argValues, el[1:])
continue
}
for k, v := range argValues {
queryValues.Set(k, v)
}
url := strings.Join(pathElements, "/")
if len(queryValues) > 0 {
url += "?" + queryValues.Encode()
}
method := route.Method
star := false
if route.Method == "*" {
method = "GET"
star = true
}
return &ActionDefinition{
Url: url,
Method: method,
Star: star,
Action: action,
Args: argValues,
Host: "TODO",
}
}
ERROR.Println("Failed to find reverse route:", action, argValues)
return nil
}
func RouterFilter(c *Controller, fc []Filter) {
var route *RouteMatch = MainRouter.Route(c.Request.Request)
if route == nil {
c.Result = c.NotFound("No matching route found")
return
}
if route.Action == "404" {
c.Result = c.NotFound("(intentionally)")
return
}
if err := c.SetAction(route.ControllerName, route.MethodName); err != nil {
c.Result = c.NotFound(err.Error())
return
}
c.Params.Route = route.Params
for i, value := range route.FixedParams {
if c.Params.Fixed == nil {
c.Params.Fixed = make(url.Values)
}
if i < len(c.MethodType.Args) {
arg := c.MethodType.Args[i]
c.Params.Fixed.Set(arg.Name, value)
} else {
WARN.Println("Too many parameters to", route.Action, "trying to add", value)
break
}
}
fc[0](c, fc[1:])
}