diff --git a/app.go b/app.go index f17d9a74..c4e968d3 100644 --- a/app.go +++ b/app.go @@ -87,6 +87,9 @@ func (app *App) AutoRouter(c ControllerInterface) *App { return app } +func (app *App) UrlFor(endpoint string, values ...string) string { + return app.Handlers.UrlFor(endpoint, values...) +} func (app *App) Filter(pattern, action string, filter FilterFunc) *App { app.Handlers.AddFilter(pattern, action, filter) return app diff --git a/controller.go b/controller.go index f0141f35..0c4b62f9 100644 --- a/controller.go +++ b/controller.go @@ -16,6 +16,7 @@ import ( "net/http" "net/url" "os" + "reflect" "strconv" "strings" "time" @@ -175,6 +176,17 @@ func (c *Controller) Abort(code string) { panic(code) } +func (c *Controller) UrlFor(endpoint string, values ...string) string { + if len(endpoint) <= 0 { + return "" + } + if endpoint[0] == '.' { + return UrlFor(reflect.Indirect(reflect.ValueOf(c.AppController)).Type().Name()+endpoint, values...) + } else { + return UrlFor(endpoint, values...) + } +} + func (c *Controller) ServeJson(encoding ...bool) { var hasIndent bool var hasencoding bool diff --git a/router.go b/router.go index 3ff5c4d8..b44b6975 100644 --- a/router.go +++ b/router.go @@ -259,6 +259,121 @@ func (p *ControllerRegistor) AddFilter(pattern, action string, filter FilterFunc p.filters[action] = append(p.filters[action], mr) } +func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string { + paths := strings.Split(endpoint, ".") + if len(paths) <= 1 { + Warn("urlfor endpoint must like path.controller.method") + return "" + } + if len(values)%2 != 0 { + Warn("urlfor params must key-value pair") + return "" + } + urlv := url.Values{} + if len(values) > 0 { + key := "" + for k, v := range values { + if k%2 == 0 { + key = v + } else { + urlv.Set(key, v) + } + } + } + controllName := strings.Join(paths[:len(paths)-1], ".") + methodName := paths[len(paths)-1] + for _, route := range p.fixrouters { + if route.controllerType.Name() == controllName { + var finded bool + if inSlice(strings.ToLower(methodName), HTTPMETHOD) { + if route.hasMethod { + if m, ok := route.methods[strings.ToLower(methodName)]; ok && m != methodName { + finded = false + } else if m, ok = route.methods["*"]; ok && m != methodName { + finded = false + } else { + finded = true + } + } else { + finded = true + } + } else if route.hasMethod { + for _, md := range route.methods { + if md == methodName { + finded = true + } + } + } + if !finded { + continue + } + if len(values) > 0 { + return route.pattern + "?" + urlv.Encode() + } + return route.pattern + } + } + for _, route := range p.routers { + if route.controllerType.Name() == controllName { + var finded bool + if inSlice(strings.ToLower(methodName), HTTPMETHOD) { + if route.hasMethod { + if m, ok := route.methods[strings.ToLower(methodName)]; ok && m != methodName { + finded = false + } else if m, ok = route.methods["*"]; ok && m != methodName { + finded = false + } else { + finded = true + } + } else { + finded = true + } + } else if route.hasMethod { + for _, md := range route.methods { + if md == methodName { + finded = true + } + } + } + if !finded { + continue + } + var returnurl string + var i int + var startreg bool + for _, v := range route.regex.String() { + if v == '(' { + startreg = true + continue + } else if v == ')' { + startreg = false + returnurl = returnurl + urlv.Get(route.params[i]) + i++ + } else if !startreg { + returnurl = string(append([]rune(returnurl), v)) + } + } + if route.regex.MatchString(returnurl) { + return returnurl + } + } + } + if p.enableAuto { + for cName, methodList := range p.autoRouter { + if strings.ToLower(strings.TrimSuffix(paths[len(paths)-2], "Controller")) == cName { + if _, ok := methodList[methodName]; ok { + if len(values) > 0 { + return "/" + strings.TrimSuffix(paths[len(paths)-2], "Controller") + "/" + methodName + "?" + urlv.Encode() + } else { + return "/" + strings.TrimSuffix(paths[len(paths)-2], "Controller") + "/" + methodName + } + } + } + } + } + return "" +} + // AutoRoute func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) { defer func() { diff --git a/router_test.go b/router_test.go index 96800fb8..7dcbdee1 100644 --- a/router_test.go +++ b/router_test.go @@ -25,6 +25,29 @@ func (this *TestController) Myext() { this.Ctx.Output.Body([]byte(this.Ctx.Input.Params(":ext"))) } +func (this *TestController) GetUrl() { + this.Ctx.Output.Body([]byte(this.UrlFor(".Myext"))) +} + +func TestUrlFor(t *testing.T) { + handler := NewControllerRegistor() + handler.Add("/api/list", &TestController{}, "*:List") + handler.Add("/person/:last/:first", &TestController{}) + handler.AddAuto(&TestController{}) + if handler.UrlFor("TestController.List") != "/api/list" { + t.Errorf("TestController.List must equal to /api/list") + } + if handler.UrlFor("TestController.Get", ":last", "xie", ":first", "asta") != "/person/xie/asta" { + t.Errorf("TestController.Get must equal to /person/xie/asta") + } + if handler.UrlFor("TestController.Myext") != "/Test/Myext" { + t.Errorf("TestController.Myext must equal to /Test/Myext") + } + if handler.UrlFor("TestController.GetUrl") != "/Test/GetUrl" { + t.Errorf("TestController.GetUrl must equal to /Test/GetUrl") + } +} + func TestUserFunc(t *testing.T) { r, _ := http.NewRequest("GET", "/api/list", nil) w := httptest.NewRecorder() diff --git a/template.go b/template.go index b633fef2..5cd05388 100644 --- a/template.go +++ b/template.go @@ -43,6 +43,8 @@ func init() { beegoTplFuncMap["le"] = le // <= beegoTplFuncMap["lt"] = lt // < beegoTplFuncMap["ne"] = ne // != + + beegoTplFuncMap["urlfor"] = UrlFor // != } // AddFuncMap let user to register a func in the template diff --git a/utils.go b/utils.go index 0a5f3246..0e2b8e13 100644 --- a/utils.go +++ b/utils.go @@ -385,6 +385,6 @@ func GetRandomString(n int) string { // /login // /login?next=/ // /user/John%20Doe -func UrlFor(endpoint string, values ...string) { - +func UrlFor(endpoint string, values ...string) string { + return BeeApp.UrlFor(endpoint, values...) }