e00eab7f by astaxie

beego: change to tree

1 parent bfabcfcb
...@@ -121,40 +121,7 @@ func listConf(rw http.ResponseWriter, r *http.Request) { ...@@ -121,40 +121,7 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
121 fmt.Fprintln(rw, "AdminHttpPort:", AdminHttpPort) 121 fmt.Fprintln(rw, "AdminHttpPort:", AdminHttpPort)
122 case "router": 122 case "router":
123 fmt.Fprintln(rw, "Print all router infomation:") 123 fmt.Fprintln(rw, "Print all router infomation:")
124 for _, router := range BeeApp.Handlers.fixrouters { 124 // @todo print routers
125 if router.routerType == routerTypeBeego {
126 if router.hasMethod {
127 fmt.Fprintln(rw, router.pattern, "----", router.methods, "----", router.controllerType.Name())
128 } else {
129 fmt.Fprintln(rw, router.pattern, "----", router.controllerType.Name())
130 }
131 } else if router.routerType == routerTypeRESTFul {
132 fmt.Fprintln(rw, router.pattern, "----", router.methods, "----", router.runfunction)
133 } else if router.routerType == routerTypeHandler {
134 fmt.Fprintln(rw, router.pattern, "----", router.handler)
135 }
136 }
137 for _, router := range BeeApp.Handlers.routers {
138 if router.routerType == routerTypeBeego {
139 if router.hasMethod {
140 fmt.Fprintln(rw, router.pattern, "----", router.methods, "----", router.controllerType.Name())
141 } else {
142 fmt.Fprintln(rw, router.pattern, "----", router.controllerType.Name())
143 }
144 } else if router.routerType == routerTypeRESTFul {
145 fmt.Fprintln(rw, router.pattern, "----", router.methods, "----", router.runfunction)
146 } else if router.routerType == routerTypeHandler {
147 fmt.Fprintln(rw, router.pattern, "----", router.handler)
148 }
149 }
150 if BeeApp.Handlers.enableAuto {
151 for controllerName, methodObj := range BeeApp.Handlers.autoRouter {
152 fmt.Fprintln(rw, controllerName, "----")
153 for methodName, obj := range methodObj {
154 fmt.Fprintln(rw, " ", methodName, "-----", obj.Name())
155 }
156 }
157 }
158 case "filter": 125 case "filter":
159 fmt.Fprintln(rw, "Print all filter infomation:") 126 fmt.Fprintln(rw, "Print all filter infomation:")
160 if BeeApp.Handlers.enableFilter { 127 if BeeApp.Handlers.enableFilter {
...@@ -164,12 +131,6 @@ func listConf(rw http.ResponseWriter, r *http.Request) { ...@@ -164,12 +131,6 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
164 fmt.Fprintln(rw, f.pattern, utils.GetFuncName(f.filterFunc)) 131 fmt.Fprintln(rw, f.pattern, utils.GetFuncName(f.filterFunc))
165 } 132 }
166 } 133 }
167 fmt.Fprintln(rw, "AfterStatic:")
168 if bf, ok := BeeApp.Handlers.filters[AfterStatic]; ok {
169 for _, f := range bf {
170 fmt.Fprintln(rw, f.pattern, utils.GetFuncName(f.filterFunc))
171 }
172 }
173 fmt.Fprintln(rw, "BeforeExec:") 134 fmt.Fprintln(rw, "BeforeExec:")
174 if bf, ok := BeeApp.Handlers.filters[BeforeExec]; ok { 135 if bf, ok := BeeApp.Handlers.filters[BeforeExec]; ok {
175 for _, f := range bf { 136 for _, f := range bf {
......
...@@ -100,7 +100,7 @@ func AddGroupRouter(prefix string, groups GroupRouters) *App { ...@@ -100,7 +100,7 @@ func AddGroupRouter(prefix string, groups GroupRouters) *App {
100 // 100 //
101 // regex router 101 // regex router
102 // 102 //
103 // beego.Router(“/api/:id([0-9]+)“, &controllers.RController{}) 103 // beego.Router("/api/:id([0-9]+)", &controllers.RController{})
104 // 104 //
105 // custom rules 105 // custom rules
106 // beego.Router("/api/list",&RestController{},"*:ListFood") 106 // beego.Router("/api/list",&RestController{},"*:ListFood")
...@@ -112,6 +112,38 @@ func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *A ...@@ -112,6 +112,38 @@ func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *A
112 return BeeApp 112 return BeeApp
113 } 113 }
114 114
115 // Router add list from
116 // usage:
117 // beego.Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{})
118 // type BankAccount struct{
119 // beego.Controller
120 // }
121 //
122 // register the function
123 // func (b *BankAccount)Mapping(){
124 // b.Mapping("ShowAccount" , b.ShowAccount)
125 // b.Mapping("ModifyAccount", b.ModifyAccount)
126 //}
127 //
128 // //@router /account/:id [get]
129 // func (b *BankAccount) ShowAccount(){
130 // //logic
131 // }
132 //
133 //
134 // //@router /account/:id [post]
135 // func (b *BankAccount) ModifyAccount(){
136 // //logic
137 // }
138 //
139 // the comments @router url methodlist
140 // url support all the function Router's pattern
141 // methodlist [get post head put delete options *]
142 func Include(cList ...ControllerInterface) *App {
143 BeeApp.Handlers.Include(cList...)
144 return BeeApp
145 }
146
115 // RESTRouter adds a restful controller handler to BeeApp. 147 // RESTRouter adds a restful controller handler to BeeApp.
116 // its' controller implements beego.ControllerInterface and 148 // its' controller implements beego.ControllerInterface and
117 // defines a param "pattern/:objectId" to visit each resource. 149 // defines a param "pattern/:objectId" to visit each resource.
...@@ -261,14 +293,6 @@ func DelStaticPath(url string) *App { ...@@ -261,14 +293,6 @@ func DelStaticPath(url string) *App {
261 return BeeApp 293 return BeeApp
262 } 294 }
263 295
264 // [Deprecated] use InsertFilter.
265 // Filter adds a FilterFunc under pattern condition and named action.
266 // The actions contains BeforeRouter,AfterStatic,BeforeExec,AfterExec and FinishRouter.
267 func AddFilter(pattern, action string, filter FilterFunc) *App {
268 BeeApp.Handlers.AddFilter(pattern, action, filter)
269 return BeeApp
270 }
271
272 // InsertFilter adds a FilterFunc with pattern condition and action constant. 296 // InsertFilter adds a FilterFunc with pattern condition and action constant.
273 // The pos means action constant including 297 // The pos means action constant including
274 // beego.BeforeRouter, beego.AfterStatic, beego.BeforeExec, beego.AfterExec and beego.FinishRouter. 298 // beego.BeforeRouter, beego.AfterStatic, beego.BeforeExec, beego.AfterExec and beego.FinishRouter.
......
...@@ -34,7 +34,8 @@ const ( ...@@ -34,7 +34,8 @@ const (
34 34
35 var ( 35 var (
36 // custom error when user stop request handler manually. 36 // custom error when user stop request handler manually.
37 USERSTOPRUN = errors.New("User stop run") 37 USERSTOPRUN = errors.New("User stop run")
38 GlobalControllerRouter map[string]map[string]*Tree //pkgpath+controller:method:routertree
38 ) 39 )
39 40
40 // Controller defines some basic http request handler operations, such as 41 // Controller defines some basic http request handler operations, such as
...@@ -55,6 +56,7 @@ type Controller struct { ...@@ -55,6 +56,7 @@ type Controller struct {
55 AppController interface{} 56 AppController interface{}
56 EnableRender bool 57 EnableRender bool
57 EnableXSRF bool 58 EnableXSRF bool
59 Routers map[string]*Tree //method:routertree
58 } 60 }
59 61
60 // ControllerInterface is an interface to uniform all controller handler. 62 // ControllerInterface is an interface to uniform all controller handler.
...@@ -72,6 +74,8 @@ type ControllerInterface interface { ...@@ -72,6 +74,8 @@ type ControllerInterface interface {
72 Render() error 74 Render() error
73 XsrfToken() string 75 XsrfToken() string
74 CheckXsrfCookie() bool 76 CheckXsrfCookie() bool
77 HandlerFunc(fn interface{})
78 URLMapping()
75 } 79 }
76 80
77 // Init generates default values of controller operations. 81 // Init generates default values of controller operations.
...@@ -86,6 +90,7 @@ func (c *Controller) Init(ctx *context.Context, controllerName, actionName strin ...@@ -86,6 +90,7 @@ func (c *Controller) Init(ctx *context.Context, controllerName, actionName strin
86 c.EnableRender = true 90 c.EnableRender = true
87 c.EnableXSRF = true 91 c.EnableXSRF = true
88 c.Data = ctx.Input.Data 92 c.Data = ctx.Input.Data
93 c.Routers = make(map[string]*Tree)
89 } 94 }
90 95
91 // Prepare runs after Init before request function execution. 96 // Prepare runs after Init before request function execution.
...@@ -133,6 +138,32 @@ func (c *Controller) Options() { ...@@ -133,6 +138,32 @@ func (c *Controller) Options() {
133 http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405) 138 http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
134 } 139 }
135 140
141 // call function fn
142 func (c *Controller) HandlerFunc(fn interface{}) {
143 if v, ok := fn.(func()); ok {
144 v()
145 }
146 }
147
148 // URLMapping register the internal Controller router.
149 func (c *Controller) URLMapping() {
150 }
151
152 func (c *Controller) Mapping(method, pattern string, fn func()) {
153 method = strings.ToLower(method)
154 if !utils.InSlice(method, HTTPMETHOD) && method != "*" {
155 Critical("add mapping method:" + method + " is a valid method")
156 return
157 }
158 if t, ok := c.Routers[method]; ok {
159 t.AddRouter(pattern, fn)
160 } else {
161 t = NewTree()
162 t.AddRouter(pattern, fn)
163 c.Routers[method] = t
164 }
165 }
166
136 // Render sends the response with rendered template bytes as text/html type. 167 // Render sends the response with rendered template bytes as text/html type.
137 func (c *Controller) Render() error { 168 func (c *Controller) Render() error {
138 if !c.EnableRender { 169 if !c.EnableRender {
...@@ -295,7 +326,6 @@ func (c *Controller) ServeXml() { ...@@ -295,7 +326,6 @@ func (c *Controller) ServeXml() {
295 } 326 }
296 327
297 // ServeFormatted serve Xml OR Json, depending on the value of the Accept header 328 // ServeFormatted serve Xml OR Json, depending on the value of the Accept header
298
299 func (c *Controller) ServeFormatted() { 329 func (c *Controller) ServeFormatted() {
300 accept := c.Ctx.Input.Header("Accept") 330 accept := c.Ctx.Input.Header("Accept")
301 switch accept { 331 switch accept {
......
...@@ -6,51 +6,24 @@ ...@@ -6,51 +6,24 @@
6 6
7 package beego 7 package beego
8 8
9 import "regexp"
10
11 // FilterRouter defines filter operation before controller handler execution. 9 // FilterRouter defines filter operation before controller handler execution.
12 // it can match patterned url and do filter function when action arrives. 10 // it can match patterned url and do filter function when action arrives.
13 type FilterRouter struct { 11 type FilterRouter struct {
14 pattern string 12 filterFunc FilterFunc
15 regex *regexp.Regexp 13 tree *Tree
16 filterFunc FilterFunc 14 pattern string
17 hasregex bool
18 params map[int]string
19 parseParams map[string]string
20 } 15 }
21 16
22 // ValidRouter check current request is valid for this filter. 17 // ValidRouter check current request is valid for this filter.
23 // if matched, returns parsed params in this request by defined filter router pattern. 18 // if matched, returns parsed params in this request by defined filter router pattern.
24 func (mr *FilterRouter) ValidRouter(router string) (bool, map[string]string) { 19 func (f *FilterRouter) ValidRouter(router string) (bool, map[string]string) {
25 if mr.pattern == "" { 20 isok, params := f.tree.Match(router)
26 return true, nil 21 if isok == nil {
27 } 22 return false, nil
28 if mr.pattern == "*" {
29 return true, nil
30 }
31 if router == mr.pattern {
32 return true, nil
33 } 23 }
34 //pattern /admin router /admin/ match 24 if isok, ok := isok.(bool); ok {
35 //pattern /admin/ router /admin don't match, because url will 301 in router 25 return isok, params
36 if n := len(router); n > 1 && router[n-1] == '/' && router[:n-2] == mr.pattern { 26 } else {
37 return true, nil 27 return false, nil
38 }
39
40 if mr.hasregex {
41 if !mr.regex.MatchString(router) {
42 return false, nil
43 }
44 matches := mr.regex.FindStringSubmatch(router)
45 if len(matches) > 0 {
46 if len(matches[0]) == len(router) {
47 params := make(map[string]string)
48 for i, match := range matches[1:] {
49 params[mr.params[i]] = match
50 }
51 return true, params
52 }
53 }
54 } 28 }
55 return false, nil
56 } 29 }
......
...@@ -22,7 +22,7 @@ func TestFilter(t *testing.T) { ...@@ -22,7 +22,7 @@ func TestFilter(t *testing.T) {
22 r, _ := http.NewRequest("GET", "/person/asta/Xie", nil) 22 r, _ := http.NewRequest("GET", "/person/asta/Xie", nil)
23 w := httptest.NewRecorder() 23 w := httptest.NewRecorder()
24 handler := NewControllerRegistor() 24 handler := NewControllerRegistor()
25 handler.AddFilter("/person/:last/:first", "AfterStatic", FilterUser) 25 handler.InsertFilter("/person/:last/:first", BeforeRouter, FilterUser)
26 handler.Add("/person/:last/:first", &TestController{}) 26 handler.Add("/person/:last/:first", &TestController{})
27 handler.ServeHTTP(w, r) 27 handler.ServeHTTP(w, r)
28 if w.Body.String() != "i am astaXie" { 28 if w.Body.String() != "i am astaXie" {
...@@ -41,7 +41,7 @@ func TestPatternTwo(t *testing.T) { ...@@ -41,7 +41,7 @@ func TestPatternTwo(t *testing.T) {
41 r, _ := http.NewRequest("GET", "/admin/", nil) 41 r, _ := http.NewRequest("GET", "/admin/", nil)
42 w := httptest.NewRecorder() 42 w := httptest.NewRecorder()
43 handler := NewControllerRegistor() 43 handler := NewControllerRegistor()
44 handler.AddFilter("/admin/:all", "AfterStatic", FilterAdminUser) 44 handler.InsertFilter("/admin/?:all", BeforeRouter, FilterAdminUser)
45 handler.ServeHTTP(w, r) 45 handler.ServeHTTP(w, r)
46 if w.Body.String() != "i am admin" { 46 if w.Body.String() != "i am admin" {
47 t.Errorf("filter /admin/ can't run") 47 t.Errorf("filter /admin/ can't run")
...@@ -52,7 +52,7 @@ func TestPatternThree(t *testing.T) { ...@@ -52,7 +52,7 @@ func TestPatternThree(t *testing.T) {
52 r, _ := http.NewRequest("GET", "/admin/astaxie", nil) 52 r, _ := http.NewRequest("GET", "/admin/astaxie", nil)
53 w := httptest.NewRecorder() 53 w := httptest.NewRecorder()
54 handler := NewControllerRegistor() 54 handler := NewControllerRegistor()
55 handler.AddFilter("/admin/:all", "AfterStatic", FilterAdminUser) 55 handler.InsertFilter("/admin/:all", BeforeRouter, FilterAdminUser)
56 handler.ServeHTTP(w, r) 56 handler.ServeHTTP(w, r)
57 if w.Body.String() != "i am admin" { 57 if w.Body.String() != "i am admin" {
58 t.Errorf("filter /admin/astaxie can't run") 58 t.Errorf("filter /admin/astaxie can't run")
......
...@@ -7,18 +7,17 @@ package beego ...@@ -7,18 +7,17 @@ package beego
7 7
8 import ( 8 import (
9 "net/http" 9 "net/http"
10 "strings"
11 10
12 beecontext "github.com/astaxie/beego/context" 11 beecontext "github.com/astaxie/beego/context"
12 "github.com/astaxie/beego/middleware"
13 ) 13 )
14 14
15 type namespaceCond func(*beecontext.Context) bool 15 type namespaceCond func(*beecontext.Context) bool
16 16
17 // Namespace is store all the info 17 // Namespace is store all the info
18 type Namespace struct { 18 type Namespace struct {
19 prefix string 19 prefix string
20 condition namespaceCond 20 handlers *ControllerRegistor
21 handlers *ControllerRegistor
22 } 21 }
23 22
24 // get new Namespace 23 // get new Namespace
...@@ -39,8 +38,23 @@ func NewNamespace(prefix string) *Namespace { ...@@ -39,8 +38,23 @@ func NewNamespace(prefix string) *Namespace {
39 // } 38 // }
40 // return false 39 // return false
41 // }) 40 // })
41 // Cond as the first filter
42 func (n *Namespace) Cond(cond namespaceCond) *Namespace { 42 func (n *Namespace) Cond(cond namespaceCond) *Namespace {
43 n.condition = cond 43 fn := func(ctx *beecontext.Context) {
44 if !cond(ctx) {
45 middleware.Exception("405", ctx.ResponseWriter, ctx.Request, "Method not allowed")
46 }
47 }
48 if v, ok := n.handlers.filters[BeforeRouter]; ok {
49 mr := new(FilterRouter)
50 mr.tree = NewTree()
51 mr.pattern = "*"
52 mr.filterFunc = fn
53 mr.tree.AddRouter("*", true)
54 n.handlers.filters[BeforeRouter] = append([]*FilterRouter{mr}, v...)
55 } else {
56 n.handlers.InsertFilter("*", BeforeRouter, fn)
57 }
44 return n 58 return n
45 } 59 }
46 60
...@@ -55,12 +69,13 @@ func (n *Namespace) Cond(cond namespaceCond) *Namespace { ...@@ -55,12 +69,13 @@ func (n *Namespace) Cond(cond namespaceCond) *Namespace {
55 // } 69 // }
56 // }) 70 // })
57 func (n *Namespace) Filter(action string, filter FilterFunc) *Namespace { 71 func (n *Namespace) Filter(action string, filter FilterFunc) *Namespace {
72 var a int
58 if action == "before" { 73 if action == "before" {
59 action = "BeforeRouter" 74 a = BeforeRouter
60 } else if action == "after" { 75 } else if action == "after" {
61 action = "FinishRouter" 76 a = FinishRouter
62 } 77 }
63 n.handlers.AddFilter("*", action, filter) 78 n.handlers.InsertFilter("*", a, filter)
64 return n 79 return n
65 } 80 }
66 81
...@@ -167,39 +182,35 @@ func (n *Namespace) Handler(rootpath string, h http.Handler) *Namespace { ...@@ -167,39 +182,35 @@ func (n *Namespace) Handler(rootpath string, h http.Handler) *Namespace {
167 //) 182 //)
168 func (n *Namespace) Namespace(ns ...*Namespace) *Namespace { 183 func (n *Namespace) Namespace(ns ...*Namespace) *Namespace {
169 for _, ni := range ns { 184 for _, ni := range ns {
170 n.handlers.Handler(ni.prefix, ni, true) 185 n.handlers.routers.AddTree(ni.prefix, ni.handlers.routers)
186 if n.handlers.enableFilter {
187 for pos, filterList := range ni.handlers.filters {
188 for _, mr := range filterList {
189 t := NewTree()
190 t.AddTree(ni.prefix, mr.tree)
191 mr.tree = t
192 n.handlers.insertFilterRouter(pos, mr)
193 }
194 }
195 }
171 } 196 }
172 return n 197 return n
173 } 198 }
174 199
175 // Namespace implement the http.Handler
176 func (n *Namespace) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
177 //trim the preifix from URL.Path
178 r.URL.Path = strings.TrimPrefix(r.URL.Path, n.prefix)
179 // init context
180 context := &beecontext.Context{
181 ResponseWriter: rw,
182 Request: r,
183 Input: beecontext.NewInput(r),
184 Output: beecontext.NewOutput(),
185 }
186 context.Output.Context = context
187 context.Output.EnableGzip = EnableGzip
188
189 if context.Input.IsWebsocket() {
190 context.ResponseWriter = rw
191 }
192 if n.condition != nil && !n.condition(context) {
193 http.Error(rw, "Method Not Allowed", 405)
194 return
195 }
196 n.handlers.ServeHTTP(rw, r)
197 }
198
199 // register Namespace into beego.Handler 200 // register Namespace into beego.Handler
200 // support multi Namespace 201 // support multi Namespace
201 func AddNamespace(nl ...*Namespace) { 202 func AddNamespace(nl ...*Namespace) {
202 for _, n := range nl { 203 for _, n := range nl {
203 Handler(n.prefix, n, true) 204 BeeApp.Handlers.routers.AddTree(n.prefix, n.handlers.routers)
205 if n.handlers.enableFilter {
206 for pos, filterList := range n.handlers.filters {
207 for _, mr := range filterList {
208 t := NewTree()
209 t.AddTree(n.prefix, mr.tree)
210 mr.tree = t
211 BeeApp.Handlers.insertFilterRouter(pos, mr)
212 }
213 }
214 }
204 } 215 }
205 } 216 }
......
...@@ -23,7 +23,8 @@ func TestNamespaceGet(t *testing.T) { ...@@ -23,7 +23,8 @@ func TestNamespaceGet(t *testing.T) {
23 ns.Get("/user", func(ctx *context.Context) { 23 ns.Get("/user", func(ctx *context.Context) {
24 ctx.Output.Body([]byte("v1_user")) 24 ctx.Output.Body([]byte("v1_user"))
25 }) 25 })
26 ns.ServeHTTP(w, r) 26 AddNamespace(ns)
27 BeeApp.Handlers.ServeHTTP(w, r)
27 if w.Body.String() != "v1_user" { 28 if w.Body.String() != "v1_user" {
28 t.Errorf("TestNamespaceGet can't run, get the response is " + w.Body.String()) 29 t.Errorf("TestNamespaceGet can't run, get the response is " + w.Body.String())
29 } 30 }
...@@ -37,7 +38,8 @@ func TestNamespacePost(t *testing.T) { ...@@ -37,7 +38,8 @@ func TestNamespacePost(t *testing.T) {
37 ns.Post("/user/:id", func(ctx *context.Context) { 38 ns.Post("/user/:id", func(ctx *context.Context) {
38 ctx.Output.Body([]byte(ctx.Input.Param(":id"))) 39 ctx.Output.Body([]byte(ctx.Input.Param(":id")))
39 }) 40 })
40 ns.ServeHTTP(w, r) 41 AddNamespace(ns)
42 BeeApp.Handlers.ServeHTTP(w, r)
41 if w.Body.String() != "123" { 43 if w.Body.String() != "123" {
42 t.Errorf("TestNamespacePost can't run, get the response is " + w.Body.String()) 44 t.Errorf("TestNamespacePost can't run, get the response is " + w.Body.String())
43 } 45 }
...@@ -54,7 +56,8 @@ func TestNamespaceNest(t *testing.T) { ...@@ -54,7 +56,8 @@ func TestNamespaceNest(t *testing.T) {
54 ctx.Output.Body([]byte("order")) 56 ctx.Output.Body([]byte("order"))
55 }), 57 }),
56 ) 58 )
57 ns.ServeHTTP(w, r) 59 AddNamespace(ns)
60 BeeApp.Handlers.ServeHTTP(w, r)
58 if w.Body.String() != "order" { 61 if w.Body.String() != "order" {
59 t.Errorf("TestNamespaceNest can't run, get the response is " + w.Body.String()) 62 t.Errorf("TestNamespaceNest can't run, get the response is " + w.Body.String())
60 } 63 }
...@@ -71,36 +74,21 @@ func TestNamespaceNestParam(t *testing.T) { ...@@ -71,36 +74,21 @@ func TestNamespaceNestParam(t *testing.T) {
71 ctx.Output.Body([]byte(ctx.Input.Param(":id"))) 74 ctx.Output.Body([]byte(ctx.Input.Param(":id")))
72 }), 75 }),
73 ) 76 )
74 ns.ServeHTTP(w, r) 77 AddNamespace(ns)
78 BeeApp.Handlers.ServeHTTP(w, r)
75 if w.Body.String() != "123" { 79 if w.Body.String() != "123" {
76 t.Errorf("TestNamespaceNestParam can't run, get the response is " + w.Body.String()) 80 t.Errorf("TestNamespaceNestParam can't run, get the response is " + w.Body.String())
77 } 81 }
78 } 82 }
79 83
80 func TestNamespaceFilter(t *testing.T) {
81 r, _ := http.NewRequest("GET", "/v1/user/123", nil)
82 w := httptest.NewRecorder()
83
84 ns := NewNamespace("/v1")
85 ns.Filter("before", func(ctx *context.Context) {
86 ctx.Output.Body([]byte("this is Filter"))
87 }).
88 Get("/user/:id", func(ctx *context.Context) {
89 ctx.Output.Body([]byte(ctx.Input.Param(":id")))
90 })
91 ns.ServeHTTP(w, r)
92 if w.Body.String() != "this is Filter" {
93 t.Errorf("TestNamespaceFilter can't run, get the response is " + w.Body.String())
94 }
95 }
96
97 func TestNamespaceRouter(t *testing.T) { 84 func TestNamespaceRouter(t *testing.T) {
98 r, _ := http.NewRequest("GET", "/v1/api/list", nil) 85 r, _ := http.NewRequest("GET", "/v1/api/list", nil)
99 w := httptest.NewRecorder() 86 w := httptest.NewRecorder()
100 87
101 ns := NewNamespace("/v1") 88 ns := NewNamespace("/v1")
102 ns.Router("/api/list", &TestController{}, "*:List") 89 ns.Router("/api/list", &TestController{}, "*:List")
103 ns.ServeHTTP(w, r) 90 AddNamespace(ns)
91 BeeApp.Handlers.ServeHTTP(w, r)
104 if w.Body.String() != "i am list" { 92 if w.Body.String() != "i am list" {
105 t.Errorf("TestNamespaceRouter can't run, get the response is " + w.Body.String()) 93 t.Errorf("TestNamespaceRouter can't run, get the response is " + w.Body.String())
106 } 94 }
...@@ -112,17 +100,36 @@ func TestNamespaceAutoFunc(t *testing.T) { ...@@ -112,17 +100,36 @@ func TestNamespaceAutoFunc(t *testing.T) {
112 100
113 ns := NewNamespace("/v1") 101 ns := NewNamespace("/v1")
114 ns.AutoRouter(&TestController{}) 102 ns.AutoRouter(&TestController{})
115 ns.ServeHTTP(w, r) 103 AddNamespace(ns)
104 BeeApp.Handlers.ServeHTTP(w, r)
116 if w.Body.String() != "i am list" { 105 if w.Body.String() != "i am list" {
117 t.Errorf("user define func can't run") 106 t.Errorf("user define func can't run")
118 } 107 }
119 } 108 }
120 109
121 func TestNamespaceCond(t *testing.T) { 110 func TestNamespaceFilter(t *testing.T) {
122 r, _ := http.NewRequest("GET", "/v1/test/list", nil) 111 r, _ := http.NewRequest("GET", "/v1/user/123", nil)
123 w := httptest.NewRecorder() 112 w := httptest.NewRecorder()
124 113
125 ns := NewNamespace("/v1") 114 ns := NewNamespace("/v1")
115 ns.Filter("before", func(ctx *context.Context) {
116 ctx.Output.Body([]byte("this is Filter"))
117 }).
118 Get("/user/:id", func(ctx *context.Context) {
119 ctx.Output.Body([]byte(ctx.Input.Param(":id")))
120 })
121 AddNamespace(ns)
122 BeeApp.Handlers.ServeHTTP(w, r)
123 if w.Body.String() != "this is Filter" {
124 t.Errorf("TestNamespaceFilter can't run, get the response is " + w.Body.String())
125 }
126 }
127
128 func TestNamespaceCond(t *testing.T) {
129 r, _ := http.NewRequest("GET", "/v2/test/list", nil)
130 w := httptest.NewRecorder()
131
132 ns := NewNamespace("/v2")
126 ns.Cond(func(ctx *context.Context) bool { 133 ns.Cond(func(ctx *context.Context) bool {
127 if ctx.Input.Domain() == "beego.me" { 134 if ctx.Input.Domain() == "beego.me" {
128 return true 135 return true
...@@ -130,7 +137,8 @@ func TestNamespaceCond(t *testing.T) { ...@@ -130,7 +137,8 @@ func TestNamespaceCond(t *testing.T) {
130 return false 137 return false
131 }). 138 }).
132 AutoRouter(&TestController{}) 139 AutoRouter(&TestController{})
133 ns.ServeHTTP(w, r) 140 AddNamespace(ns)
141 BeeApp.Handlers.ServeHTTP(w, r)
134 if w.Code != 405 { 142 if w.Code != 405 {
135 t.Errorf("TestNamespaceCond can't run get the result " + strconv.Itoa(w.Code)) 143 t.Errorf("TestNamespaceCond can't run get the result " + strconv.Itoa(w.Code))
136 } 144 }
......
1 // Beego (http://beego.me/)
2 // @description beego is an open-source, high-performance web framework for the Go programming language.
3 // @link http://github.com/astaxie/beego for the canonical source repository
4 // @license http://github.com/astaxie/beego/blob/master/LICENSE
5 // @authors astaxie
6 package beego
...@@ -12,9 +12,8 @@ import ( ...@@ -12,9 +12,8 @@ import (
12 "fmt" 12 "fmt"
13 "net" 13 "net"
14 "net/http" 14 "net/http"
15 "net/url" 15 "path"
16 "reflect" 16 "reflect"
17 "regexp"
18 "runtime" 17 "runtime"
19 "strconv" 18 "strconv"
20 "strings" 19 "strings"
...@@ -29,7 +28,6 @@ import ( ...@@ -29,7 +28,6 @@ import (
29 const ( 28 const (
30 // default filter execution points 29 // default filter execution points
31 BeforeRouter = iota 30 BeforeRouter = iota
32 AfterStatic
33 BeforeExec 31 BeforeExec
34 AfterExec 32 AfterExec
35 FinishRouter 33 FinishRouter
...@@ -60,34 +58,25 @@ func ExceptMethodAppend(action string) { ...@@ -60,34 +58,25 @@ func ExceptMethodAppend(action string) {
60 } 58 }
61 59
62 type controllerInfo struct { 60 type controllerInfo struct {
63 pattern string
64 regex *regexp.Regexp
65 params map[int]string
66 controllerType reflect.Type 61 controllerType reflect.Type
67 methods map[string]string 62 methods map[string]string
68 hasMethod bool
69 handler http.Handler 63 handler http.Handler
70 runfunction FilterFunc 64 runfunction FilterFunc
71 routerType int 65 routerType int
72 isPrefix bool
73 } 66 }
74 67
75 // ControllerRegistor containers registered router rules, controller handlers and filters. 68 // ControllerRegistor containers registered router rules, controller handlers and filters.
76 type ControllerRegistor struct { 69 type ControllerRegistor struct {
77 routers []*controllerInfo // regexp router storage 70 routers *Tree
78 fixrouters []*controllerInfo // fixed router storage
79 enableFilter bool 71 enableFilter bool
80 filters map[int][]*FilterRouter 72 filters map[int][]*FilterRouter
81 enableAuto bool
82 autoRouter map[string]map[string]reflect.Type //key:controller key:method value:reflect.type
83 } 73 }
84 74
85 // NewControllerRegistor returns a new ControllerRegistor. 75 // NewControllerRegistor returns a new ControllerRegistor.
86 func NewControllerRegistor() *ControllerRegistor { 76 func NewControllerRegistor() *ControllerRegistor {
87 return &ControllerRegistor{ 77 return &ControllerRegistor{
88 routers: make([]*controllerInfo, 0), 78 routers: NewTree(),
89 autoRouter: make(map[string]map[string]reflect.Type), 79 filters: make(map[int][]*FilterRouter),
90 filters: make(map[int][]*FilterRouter),
91 } 80 }
92 } 81 }
93 82
...@@ -102,7 +91,6 @@ func NewControllerRegistor() *ControllerRegistor { ...@@ -102,7 +91,6 @@ func NewControllerRegistor() *ControllerRegistor {
102 // Add("/api",&RestController{},"get,post:ApiFunc") 91 // Add("/api",&RestController{},"get,post:ApiFunc")
103 // Add("/simple",&SimpleController{},"get:GetFunc;post:PostFunc") 92 // Add("/simple",&SimpleController{},"get:GetFunc;post:PostFunc")
104 func (p *ControllerRegistor) Add(pattern string, c ControllerInterface, mappingMethods ...string) { 93 func (p *ControllerRegistor) Add(pattern string, c ControllerInterface, mappingMethods ...string) {
105 j, params, parts := p.splitRoute(pattern)
106 reflectVal := reflect.ValueOf(c) 94 reflectVal := reflect.ValueOf(c)
107 t := reflect.Indirect(reflectVal).Type() 95 t := reflect.Indirect(reflectVal).Type()
108 methods := make(map[string]string) 96 methods := make(map[string]string)
...@@ -127,40 +115,23 @@ func (p *ControllerRegistor) Add(pattern string, c ControllerInterface, mappingM ...@@ -127,40 +115,23 @@ func (p *ControllerRegistor) Add(pattern string, c ControllerInterface, mappingM
127 } 115 }
128 } 116 }
129 } 117 }
130 if j == 0 {
131 //now create the Route
132 route := &controllerInfo{}
133 route.pattern = pattern
134 route.controllerType = t
135 route.methods = methods
136 route.routerType = routerTypeBeego
137 if len(methods) > 0 {
138 route.hasMethod = true
139 }
140 p.fixrouters = append(p.fixrouters, route)
141 } else { // add regexp routers
142 //recreate the url pattern, with parameters replaced
143 //by regular expressions. then compile the regex
144 pattern = strings.Join(parts, "/")
145 regex, regexErr := regexp.Compile(pattern)
146 if regexErr != nil {
147 //TODO add error handling here to avoid panic
148 panic(regexErr)
149 }
150 118
151 //now create the Route 119 route := &controllerInfo{}
120 route.methods = methods
121 route.routerType = routerTypeBeego
122 route.controllerType = t
123 p.routers.AddRouter(pattern, route)
124 }
152 125
153 route := &controllerInfo{} 126 // only when the Runmode is dev will generate router file in the router/auto.go from the controller
154 route.regex = regex 127 // Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{})
155 route.params = params 128 func (p *ControllerRegistor) Include(cList ...ControllerInterface) {
156 route.pattern = pattern 129 if RunMode == "dev" {
157 route.methods = methods 130 for _, c := range cList {
158 route.routerType = routerTypeBeego 131 reflectVal := reflect.ValueOf(c)
159 if len(methods) > 0 { 132 t := reflect.Indirect(reflectVal).Type()
160 route.hasMethod = true 133 t.PkgPath()
161 } 134 }
162 route.controllerType = t
163 p.routers = append(p.routers, route)
164 } 135 }
165 } 136 }
166 137
...@@ -257,146 +228,20 @@ func (p *ControllerRegistor) AddMethod(method, pattern string, f FilterFunc) { ...@@ -257,146 +228,20 @@ func (p *ControllerRegistor) AddMethod(method, pattern string, f FilterFunc) {
257 methods[method] = method 228 methods[method] = method
258 } 229 }
259 route.methods = methods 230 route.methods = methods
260 paramnums, params, parts := p.splitRoute(pattern) 231 p.routers.AddRouter(pattern, route)
261 if paramnums == 0 {
262 //now create the Route
263 route.pattern = pattern
264 p.fixrouters = append(p.fixrouters, route)
265 } else {
266 //recreate the url pattern, with parameters replaced
267 //by regular expressions. then compile the regex
268 pattern = strings.Join(parts, "/")
269 regex, regexErr := regexp.Compile(pattern)
270 if regexErr != nil {
271 panic(regexErr)
272 }
273 //now create the Route
274 route.regex = regex
275 route.params = params
276 route.pattern = pattern
277 p.routers = append(p.routers, route)
278 }
279 } 232 }
280 233
234 // add user defined Handler
281 func (p *ControllerRegistor) Handler(pattern string, h http.Handler, options ...interface{}) { 235 func (p *ControllerRegistor) Handler(pattern string, h http.Handler, options ...interface{}) {
282 paramnums, params, parts := p.splitRoute(pattern)
283 route := &controllerInfo{} 236 route := &controllerInfo{}
284 route.routerType = routerTypeHandler 237 route.routerType = routerTypeHandler
285 route.handler = h 238 route.handler = h
286 if len(options) > 0 { 239 if len(options) > 0 {
287 if v, ok := options[0].(bool); ok { 240 if _, ok := options[0].(bool); ok {
288 route.isPrefix = v 241 pattern = path.Join(pattern, "?:all")
289 }
290 }
291 if paramnums == 0 {
292 route.pattern = pattern
293 p.fixrouters = append(p.fixrouters, route)
294 } else {
295 //recreate the url pattern, with parameters replaced
296 //by regular expressions. then compile the regex
297 pattern = strings.Join(parts, "/")
298 regex, regexErr := regexp.Compile(pattern)
299 if regexErr != nil {
300 panic(regexErr)
301 }
302 //now create the Route
303 route.regex = regex
304 route.params = params
305 route.pattern = pattern
306 p.routers = append(p.routers, route)
307 }
308 }
309
310 // analisys the patter to params & parts
311 func (p *ControllerRegistor) splitRoute(pattern string) (paramnums int, params map[int]string, parts []string) {
312 parts = strings.Split(pattern, "/")
313 j := 0
314 params = make(map[int]string)
315 for i, part := range parts {
316 if strings.HasPrefix(part, ":") {
317 expr := "(.*)"
318 //a user may choose to override the defult expression
319 // similar to expressjs: ‘/user/:id([0-9]+)’
320 if index := strings.Index(part, "("); index != -1 {
321 expr = part[index:]
322 part = part[:index]
323 //match /user/:id:int ([0-9]+)
324 //match /post/:username:string ([\w]+)
325 } else if lindex := strings.LastIndex(part, ":"); lindex != 0 {
326 switch part[lindex:] {
327 case ":int":
328 expr = "([0-9]+)"
329 part = part[:lindex]
330 case ":string":
331 expr = `([\w]+)`
332 part = part[:lindex]
333 }
334 //marth /user/:id! non-empty value
335 } else if part[len(part)-1] == '!' {
336 expr = `(.+)`
337 part = part[:len(part)-1]
338 }
339 params[j] = part
340 parts[i] = expr
341 j++
342 }
343 if strings.HasPrefix(part, "*") {
344 expr := "(.*)"
345 if part == "*.*" {
346 params[j] = ":path"
347 parts[i] = `([^.]+)\.([^.]+)`
348 j++
349 params[j] = ":ext"
350 j++
351 } else {
352 params[j] = ":splat"
353 parts[i] = expr
354 j++
355 }
356 }
357 //url like someprefix:id(xxx).html
358 if strings.Contains(part, ":") && strings.Contains(part, "(") && strings.Contains(part, ")") {
359 var out []rune
360 var start bool
361 var startexp bool
362 var param []rune
363 var expt []rune
364 for _, v := range part {
365 if start {
366 if v != '(' {
367 param = append(param, v)
368 continue
369 }
370 }
371 if startexp {
372 if v != ')' {
373 expt = append(expt, v)
374 continue
375 }
376 }
377 if v == ':' {
378 param = make([]rune, 0)
379 param = append(param, ':')
380 start = true
381 } else if v == '(' {
382 startexp = true
383 start = false
384 params[j] = string(param)
385 j++
386 expt = make([]rune, 0)
387 expt = append(expt, '(')
388 } else if v == ')' {
389 startexp = false
390 expt = append(expt, ')')
391 out = append(out, expt...)
392 } else {
393 out = append(out, v)
394 }
395 }
396 parts[i] = string(out)
397 } 242 }
398 } 243 }
399 return j, params, parts 244 p.routers.AddRouter(pattern, route)
400 } 245 }
401 246
402 // Add auto router to ControllerRegistor. 247 // Add auto router to ControllerRegistor.
...@@ -405,21 +250,7 @@ func (p *ControllerRegistor) splitRoute(pattern string) (paramnums int, params m ...@@ -405,21 +250,7 @@ func (p *ControllerRegistor) splitRoute(pattern string) (paramnums int, params m
405 // visit the url /main/list to execute List function 250 // visit the url /main/list to execute List function
406 // /main/page to execute Page function. 251 // /main/page to execute Page function.
407 func (p *ControllerRegistor) AddAuto(c ControllerInterface) { 252 func (p *ControllerRegistor) AddAuto(c ControllerInterface) {
408 p.enableAuto = true 253 p.AddAutoPrefix("/", c)
409 reflectVal := reflect.ValueOf(c)
410 rt := reflectVal.Type()
411 ct := reflect.Indirect(reflectVal).Type()
412 firstParam := strings.ToLower(strings.TrimSuffix(ct.Name(), "Controller"))
413 if _, ok := p.autoRouter[firstParam]; ok {
414 return
415 } else {
416 p.autoRouter[firstParam] = make(map[string]reflect.Type)
417 }
418 for i := 0; i < rt.NumMethod(); i++ {
419 if !utils.InSlice(rt.Method(i).Name, exceptMethod) {
420 p.autoRouter[firstParam][rt.Method(i).Name] = ct
421 }
422 }
423 } 254 }
424 255
425 // Add auto router to ControllerRegistor with prefix. 256 // Add auto router to ControllerRegistor with prefix.
...@@ -428,78 +259,39 @@ func (p *ControllerRegistor) AddAuto(c ControllerInterface) { ...@@ -428,78 +259,39 @@ func (p *ControllerRegistor) AddAuto(c ControllerInterface) {
428 // visit the url /admin/main/list to execute List function 259 // visit the url /admin/main/list to execute List function
429 // /admin/main/page to execute Page function. 260 // /admin/main/page to execute Page function.
430 func (p *ControllerRegistor) AddAutoPrefix(prefix string, c ControllerInterface) { 261 func (p *ControllerRegistor) AddAutoPrefix(prefix string, c ControllerInterface) {
431 p.enableAuto = true
432 reflectVal := reflect.ValueOf(c) 262 reflectVal := reflect.ValueOf(c)
433 rt := reflectVal.Type() 263 rt := reflectVal.Type()
434 ct := reflect.Indirect(reflectVal).Type() 264 ct := reflect.Indirect(reflectVal).Type()
435 firstParam := strings.Trim(prefix, "/") + "/" + strings.ToLower(strings.TrimSuffix(ct.Name(), "Controller")) 265 controllerName := strings.ToLower(strings.TrimSuffix(ct.Name(), "Controller"))
436 if _, ok := p.autoRouter[firstParam]; ok {
437 return
438 } else {
439 p.autoRouter[firstParam] = make(map[string]reflect.Type)
440 }
441 for i := 0; i < rt.NumMethod(); i++ { 266 for i := 0; i < rt.NumMethod(); i++ {
442 if !utils.InSlice(rt.Method(i).Name, exceptMethod) { 267 if !utils.InSlice(rt.Method(i).Name, exceptMethod) {
443 p.autoRouter[firstParam][rt.Method(i).Name] = ct 268 route := &controllerInfo{}
269 route.routerType = routerTypeBeego
270 route.methods = map[string]string{"*": rt.Method(i).Name}
271 route.controllerType = ct
272 pattern := path.Join(prefix, controllerName, strings.ToLower(rt.Method(i).Name), "*")
273 p.routers.AddRouter(pattern, route)
444 } 274 }
445 } 275 }
446 } 276 }
447 277
448 // [Deprecated] use InsertFilter.
449 // Add FilterFunc with pattern for action.
450 func (p *ControllerRegistor) AddFilter(pattern, action string, filter FilterFunc) error {
451 mr, err := p.buildFilter(pattern, filter)
452 if err != nil {
453 return err
454 }
455
456 switch action {
457 case "BeforeRouter":
458 p.filters[BeforeRouter] = append(p.filters[BeforeRouter], mr)
459 case "AfterStatic":
460 p.filters[AfterStatic] = append(p.filters[AfterStatic], mr)
461 case "BeforeExec":
462 p.filters[BeforeExec] = append(p.filters[BeforeExec], mr)
463 case "AfterExec":
464 p.filters[AfterExec] = append(p.filters[AfterExec], mr)
465 case "FinishRouter":
466 p.filters[FinishRouter] = append(p.filters[FinishRouter], mr)
467 }
468 p.enableFilter = true
469 return nil
470 }
471
472 // Add a FilterFunc with pattern rule and action constant. 278 // Add a FilterFunc with pattern rule and action constant.
473 func (p *ControllerRegistor) InsertFilter(pattern string, pos int, filter FilterFunc) error { 279 func (p *ControllerRegistor) InsertFilter(pattern string, pos int, filter FilterFunc) error {
474 mr, err := p.buildFilter(pattern, filter) 280 mr := new(FilterRouter)
475 if err != nil { 281 mr.tree = NewTree()
476 return err 282 mr.pattern = pattern
477 } 283 mr.filterFunc = filter
284 mr.tree.AddRouter(pattern, true)
285 return p.insertFilterRouter(pos, mr)
286 }
287
288 // add Filter into
289 func (p *ControllerRegistor) insertFilterRouter(pos int, mr *FilterRouter) error {
478 p.filters[pos] = append(p.filters[pos], mr) 290 p.filters[pos] = append(p.filters[pos], mr)
479 p.enableFilter = true 291 p.enableFilter = true
480 return nil 292 return nil
481 } 293 }
482 294
483 // build the Filter by pattern
484 func (p *ControllerRegistor) buildFilter(pattern string, filter FilterFunc) (*FilterRouter, error) {
485 mr := new(FilterRouter)
486 mr.params = make(map[int]string)
487 mr.filterFunc = filter
488 j, params, parts := p.splitRoute(pattern)
489 if j != 0 {
490 pattern = strings.Join(parts, "/")
491 regex, regexErr := regexp.Compile(pattern)
492 if regexErr != nil {
493 return nil, regexErr
494 }
495 mr.regex = regex
496 mr.hasregex = true
497 }
498 mr.params = params
499 mr.pattern = pattern
500 return mr, nil
501 }
502
503 // UrlFor does another controller handler in this request function. 295 // UrlFor does another controller handler in this request function.
504 // it can access any controller method. 296 // it can access any controller method.
505 func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string { 297 func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string {
...@@ -512,170 +304,135 @@ func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string { ...@@ -512,170 +304,135 @@ func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string {
512 Warn("urlfor params must key-value pair") 304 Warn("urlfor params must key-value pair")
513 return "" 305 return ""
514 } 306 }
515 urlv := url.Values{} 307 params := make(map[string]string)
516 if len(values) > 0 { 308 if len(values) > 0 {
517 key := "" 309 key := ""
518 for k, v := range values { 310 for k, v := range values {
519 if k%2 == 0 { 311 if k%2 == 0 {
520 key = v 312 key = v
521 } else { 313 } else {
522 urlv.Set(key, v) 314 params[key] = v
523 } 315 }
524 } 316 }
525 } 317 }
526 controllName := strings.Join(paths[:len(paths)-1], ".") 318 controllName := strings.Join(paths[:len(paths)-1], ".")
527 methodName := paths[len(paths)-1] 319 methodName := paths[len(paths)-1]
528 for _, route := range p.routers { 320 ok, url := p.geturl(p.routers, "/", controllName, methodName, params)
529 if route.controllerType.Name() == controllName { 321 if ok {
530 var finded bool 322 return url
531 if utils.InSlice(strings.ToLower(methodName), HTTPMETHOD) { 323 } else {
532 if route.hasMethod { 324 return ""
533 if m, ok := route.methods[strings.ToLower(methodName)]; ok && m != methodName {
534 finded = false
535 } else if m, ok = route.methods["*"]; ok && m != methodName {
536 finded = false
537 } else {
538 finded = true
539 }
540 } else {
541 finded = true
542 }
543 } else if route.hasMethod {
544 for _, md := range route.methods {
545 if md == methodName {
546 finded = true
547 }
548 }
549 }
550 if !finded {
551 continue
552 }
553 var returnurl string
554 var i int
555 var startreg bool
556 for _, v := range route.regex.String() {
557 if v == '(' {
558 startreg = true
559 continue
560 } else if v == ')' {
561 startreg = false
562 returnurl = returnurl + urlv.Get(route.params[i])
563 i++
564 } else if !startreg {
565 returnurl = string(append([]rune(returnurl), v))
566 }
567 }
568 if route.regex.MatchString(returnurl) {
569 return returnurl
570 }
571 }
572 } 325 }
573 for _, route := range p.fixrouters { 326 }
574 if route.controllerType.Name() == controllName { 327
575 var finded bool 328 func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName string, params map[string]string) (bool, string) {
576 if utils.InSlice(strings.ToLower(methodName), HTTPMETHOD) { 329 for k, subtree := range t.fixrouters {
577 if route.hasMethod { 330 u := path.Join(url, k)
578 if m, ok := route.methods[strings.ToLower(methodName)]; ok && m != methodName { 331 ok, u := p.geturl(subtree, u, controllName, methodName, params)
579 finded = false 332 if ok {
580 } else if m, ok = route.methods["*"]; ok && m != methodName { 333 return ok, u
581 finded = false 334 }
335 }
336 if t.wildcard != nil {
337 ok, u := p.geturl(t.wildcard, url, controllName, methodName, params)
338 if ok {
339 return ok, u
340 }
341 }
342 if t.leaf != nil {
343 if c, ok := t.leaf.runObject.(*controllerInfo); ok {
344 if c.routerType == routerTypeBeego && c.controllerType.Name() == controllName {
345 find := false
346 if utils.InSlice(strings.ToLower(methodName), HTTPMETHOD) {
347 if m, ok := c.methods[strings.ToLower(methodName)]; ok && m != methodName {
348 return false, ""
349 } else if m, ok = c.methods["*"]; ok && m != methodName {
350 return false, ""
582 } else { 351 } else {
583 finded = true 352 find = true
584 } 353 }
585 } else { 354 } else {
586 finded = true 355 for _, md := range c.methods {
587 } 356 if md == methodName {
588 } else if route.hasMethod { 357 find = true
589 for _, md := range route.methods { 358 }
590 if md == methodName {
591 finded = true
592 }
593 }
594 }
595 if !finded {
596 continue
597 }
598 if len(values) > 0 {
599 return route.pattern + "?" + urlv.Encode()
600 }
601 return route.pattern
602 }
603 }
604 if p.enableAuto {
605 for cName, methodList := range p.autoRouter {
606 if strings.ToLower(strings.TrimSuffix(paths[len(paths)-2], "Controller")) == cName {
607 if _, ok := methodList[methodName]; ok {
608 if len(values) > 0 {
609 return "/" + strings.TrimSuffix(paths[len(paths)-2], "Controller") + "/" + methodName + "?" + urlv.Encode()
610 } else {
611 return "/" + strings.TrimSuffix(paths[len(paths)-2], "Controller") + "/" + methodName
612 } 359 }
613 } 360 }
614 } 361 if find {
615 } 362 if t.leaf.regexps == nil {
616 } 363 if len(t.leaf.wildcards) == 0 {
617 return "" 364 return true, url
618 } 365 }
619 366 if len(t.leaf.wildcards) == 1 {
620 // Implement http.Handler interface. 367 if v, ok := params[t.leaf.wildcards[0]]; ok {
621 func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) { 368 delete(params, t.leaf.wildcards[0])
622 defer func() { 369 return true, url + "/" + v + tourl(params)
623 if err := recover(); err != nil { 370 }
624 if err == USERSTOPRUN { 371 if t.leaf.wildcards[0] == ":splat" {
625 return 372 return true, url + tourl(params)
626 }
627 if _, ok := err.(middleware.HTTPException); ok {
628 // catch intented errors, only for HTTP 4XX and 5XX
629 } else {
630 if RunMode == "dev" {
631 if !RecoverPanic {
632 panic(err)
633 } else {
634 if ErrorsShow {
635 if handler, ok := middleware.ErrorMaps[fmt.Sprint(err)]; ok {
636 handler(rw, r)
637 return
638 } 373 }
639 } 374 }
640 var stack string 375 if len(t.leaf.wildcards) == 3 && t.leaf.wildcards[0] == "." {
641 Critical("the request url is ", r.URL.Path) 376 if p, ok := params[":path"]; ok {
642 Critical("Handler crashed with error", err) 377 if e, isok := params[":ext"]; isok {
643 for i := 1; ; i++ { 378 delete(params, ":path")
644 _, file, line, ok := runtime.Caller(i) 379 delete(params, ":ext")
645 if !ok { 380 return true, url + "/" + p + "." + e + tourl(params)
646 break 381 }
647 } 382 }
648 Critical(file, line)
649 stack = stack + fmt.Sprintln(file, line)
650 } 383 }
651 middleware.ShowErr(err, rw, r, stack) 384 canskip := false
652 } 385 for _, v := range t.leaf.wildcards {
653 } else { 386 if v == ":" {
654 if !RecoverPanic { 387 canskip = true
655 panic(err) 388 continue
389 }
390 if u, ok := params[v]; ok {
391 url += "/" + u
392 } else {
393 if canskip {
394 canskip = false
395 continue
396 } else {
397 return false, ""
398 }
399 }
400 }
401 return true, url
656 } else { 402 } else {
657 // in production model show all infomation 403 var i int
658 if ErrorsShow { 404 var startreg bool
659 handler := p.getErrorHandler(fmt.Sprint(err)) 405 url = url + "/"
660 handler(rw, r) 406 for _, v := range t.leaf.regexps.String() {
661 return 407 if v == '(' {
662 } else { 408 startreg = true
663 Critical("the request url is ", r.URL.Path) 409 continue
664 Critical("Handler crashed with error", err) 410 } else if v == ')' {
665 for i := 1; ; i++ { 411 startreg = false
666 _, file, line, ok := runtime.Caller(i) 412 if v, ok := params[t.leaf.wildcards[i]]; ok {
667 if !ok { 413 url = url + v
414 i++
415 } else {
668 break 416 break
669 } 417 }
670 Critical(file, line) 418 } else if !startreg {
419 url = string(append([]rune(url), v))
671 } 420 }
672 } 421 }
422 if t.leaf.regexps.MatchString(url) {
423 return true, url
424 }
673 } 425 }
674 } 426 }
675
676 } 427 }
677 } 428 }
678 }() 429 }
430 return false, ""
431 }
432
433 // Implement http.Handler interface.
434 func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
435 defer p.recoverPanic(rw, r)
679 436
680 starttime := time.Now() 437 starttime := time.Now()
681 requestPath := r.URL.Path 438 requestPath := r.URL.Path
...@@ -683,7 +440,6 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -683,7 +440,6 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
683 var findrouter bool 440 var findrouter bool
684 var runMethod string 441 var runMethod string
685 var routerInfo *controllerInfo 442 var routerInfo *controllerInfo
686 params := make(map[string]string)
687 443
688 w := &responseWriter{writer: rw} 444 w := &responseWriter{writer: rw}
689 w.Header().Set("Server", BeegoServerName) 445 w.Header().Set("Server", BeegoServerName)
...@@ -749,140 +505,24 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -749,140 +505,24 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
749 goto Admin 505 goto Admin
750 } 506 }
751 507
752 if do_filter(AfterStatic) {
753 goto Admin
754 }
755
756 if context.Input.RunController != nil && context.Input.RunMethod != "" { 508 if context.Input.RunController != nil && context.Input.RunMethod != "" {
757 findrouter = true 509 findrouter = true
758 runMethod = context.Input.RunMethod 510 runMethod = context.Input.RunMethod
759 runrouter = context.Input.RunController 511 runrouter = context.Input.RunController
760 } 512 }
761 513
762 //first find path from the fixrouters to Improve Performance
763 if !findrouter { 514 if !findrouter {
764 for _, route := range p.fixrouters { 515 runObject, p := p.routers.Match(requestPath)
765 n := len(requestPath) 516 if r, ok := runObject.(*controllerInfo); ok {
766 if n == 0 { 517 routerInfo = r
767 continue 518 findrouter = true
768 } 519 if splat, ok := p[":splat"]; ok {
769 if requestPath == route.pattern { 520 splatlist := strings.Split(splat, "/")
770 runMethod = p.getRunMethod(r.Method, context, route) 521 for k, v := range splatlist {
771 if runMethod != "" { 522 p[strconv.Itoa(k)] = v
772 routerInfo = route
773 runrouter = route.controllerType
774 findrouter = true
775 break
776 }
777 }
778 // pattern /admin url /admin 200 /admin/ 200
779 // pattern /admin/ url /admin 301 /admin/ 200
780 if requestPath[n-1] != '/' && requestPath+"/" == route.pattern {
781 http.Redirect(w, r, requestPath+"/", 301)
782 goto Admin
783 }
784 if requestPath[n-1] == '/' && route.pattern+"/" == requestPath {
785 runMethod = p.getRunMethod(r.Method, context, route)
786 if runMethod != "" {
787 routerInfo = route
788 runrouter = route.controllerType
789 findrouter = true
790 break
791 }
792 }
793 if route.routerType == routerTypeHandler && route.isPrefix &&
794 strings.HasPrefix(requestPath, route.pattern) {
795
796 routerInfo = route
797 runrouter = route.controllerType
798 findrouter = true
799 break
800 }
801 }
802 }
803
804 //find regex's router
805 if !findrouter {
806 //find a matching Route
807 for _, route := range p.routers {
808
809 //check if Route pattern matches url
810 if !route.regex.MatchString(requestPath) {
811 continue
812 }
813
814 //get submatches (params)
815 matches := route.regex.FindStringSubmatch(requestPath)
816
817 //double check that the Route matches the URL pattern.
818 if len(matches[0]) != len(requestPath) {
819 continue
820 }
821
822 if len(route.params) > 0 {
823 for i, match := range matches[1:] {
824 params[route.params[i]] = match
825 }
826 }
827 runMethod = p.getRunMethod(r.Method, context, route)
828 if runMethod != "" {
829 routerInfo = route
830 runrouter = route.controllerType
831 context.Input.Params = params
832 findrouter = true
833 break
834 }
835 }
836 }
837
838 if !findrouter && p.enableAuto {
839 // deal with url with diffirent ext
840 // /controller/simple
841 // /controller/simple.html
842 // /controller/simple.json
843 // /controller/simple.rss
844 lastindex := strings.LastIndex(requestPath, "/")
845 lastsub := requestPath[lastindex+1:]
846 if subindex := strings.LastIndex(lastsub, "."); subindex != -1 {
847 context.Input.Params[":ext"] = lastsub[subindex+1:]
848 r.URL.Query().Add(":ext", lastsub[subindex+1:])
849 r.URL.RawQuery = r.URL.Query().Encode()
850 requestPath = requestPath[:len(requestPath)-len(lastsub[subindex:])]
851 }
852 for cName, methodmap := range p.autoRouter {
853 // if prev already find the router break
854 if findrouter {
855 break
856 }
857 if strings.ToLower(requestPath) == "/"+cName {
858 http.Redirect(w, r, requestPath+"/", 301)
859 goto Admin
860 }
861 // if there's no action, set the default action to index
862 if strings.ToLower(requestPath) == "/"+cName+"/" {
863 requestPath = requestPath + "index"
864 }
865 // if the request path start with controllerName
866 if strings.HasPrefix(strings.ToLower(requestPath), "/"+cName+"/") {
867 for mName, controllerType := range methodmap {
868 if strings.ToLower(requestPath) == "/"+cName+"/"+strings.ToLower(mName) ||
869 (strings.HasPrefix(strings.ToLower(requestPath), "/"+cName+"/"+strings.ToLower(mName)) &&
870 requestPath[len("/"+cName+"/"+strings.ToLower(mName)):len("/"+cName+"/"+strings.ToLower(mName))+1] == "/") {
871 runrouter = controllerType
872 runMethod = mName
873 findrouter = true
874 //parse params
875 otherurl := requestPath[len("/"+cName+"/"+strings.ToLower(mName)):]
876 if len(otherurl) > 1 {
877 plist := strings.Split(otherurl, "/")
878 for k, v := range plist[1:] {
879 context.Input.Params[strconv.Itoa(k)] = v
880 }
881 }
882 break
883 }
884 } 523 }
885 } 524 }
525 context.Input.Params = p
886 } 526 }
887 } 527 }
888 528
...@@ -910,9 +550,26 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -910,9 +550,26 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
910 } else if routerInfo.routerType == routerTypeHandler { 550 } else if routerInfo.routerType == routerTypeHandler {
911 isRunable = true 551 isRunable = true
912 routerInfo.handler.ServeHTTP(rw, r) 552 routerInfo.handler.ServeHTTP(rw, r)
553 } else {
554 runrouter = routerInfo.controllerType
555 method := strings.ToLower(r.Method)
556 if method == "post" && strings.ToLower(context.Input.Query("_method")) == "put" {
557 method = "put"
558 }
559 if method == "post" && strings.ToLower(context.Input.Query("_method")) == "delete" {
560 method = "delete"
561 }
562 if m, ok := routerInfo.methods[method]; ok {
563 runMethod = m
564 } else if m, ok = routerInfo.methods["*"]; ok {
565 runMethod = m
566 } else {
567 runMethod = strings.Title(method)
568 }
913 } 569 }
914 } 570 }
915 571
572 // also defined runrouter & runMethod from filter
916 if !isRunable { 573 if !isRunable {
917 //Invoke the request handler 574 //Invoke the request handler
918 vc := reflect.New(runrouter) 575 vc := reflect.New(runrouter)
...@@ -981,6 +638,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -981,6 +638,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
981 } 638 }
982 639
983 do_filter(FinishRouter) 640 do_filter(FinishRouter)
641
984 Admin: 642 Admin:
985 //admin module record QPS 643 //admin module record QPS
986 if EnableAdmin { 644 if EnableAdmin {
...@@ -995,6 +653,64 @@ Admin: ...@@ -995,6 +653,64 @@ Admin:
995 } 653 }
996 } 654 }
997 655
656 func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Request) {
657 if err := recover(); err != nil {
658 if err == USERSTOPRUN {
659 return
660 }
661 if _, ok := err.(middleware.HTTPException); ok {
662 // catch intented errors, only for HTTP 4XX and 5XX
663 } else {
664 if RunMode == "dev" {
665 if !RecoverPanic {
666 panic(err)
667 } else {
668 if ErrorsShow {
669 if handler, ok := middleware.ErrorMaps[fmt.Sprint(err)]; ok {
670 handler(rw, r)
671 return
672 }
673 }
674 var stack string
675 Critical("the request url is ", r.URL.Path)
676 Critical("Handler crashed with error", err)
677 for i := 1; ; i++ {
678 _, file, line, ok := runtime.Caller(i)
679 if !ok {
680 break
681 }
682 Critical(file, line)
683 stack = stack + fmt.Sprintln(file, line)
684 }
685 middleware.ShowErr(err, rw, r, stack)
686 }
687 } else {
688 if !RecoverPanic {
689 panic(err)
690 } else {
691 // in production model show all infomation
692 if ErrorsShow {
693 handler := p.getErrorHandler(fmt.Sprint(err))
694 handler(rw, r)
695 return
696 } else {
697 Critical("the request url is ", r.URL.Path)
698 Critical("Handler crashed with error", err)
699 for i := 1; ; i++ {
700 _, file, line, ok := runtime.Caller(i)
701 if !ok {
702 break
703 }
704 Critical(file, line)
705 }
706 }
707 }
708 }
709
710 }
711 }
712 }
713
998 // there always should be error handler that sets error code accordingly for all unhandled errors. 714 // there always should be error handler that sets error code accordingly for all unhandled errors.
999 // in order to have custom UI for error page it's necessary to override "500" error. 715 // in order to have custom UI for error page it's necessary to override "500" error.
1000 func (p *ControllerRegistor) getErrorHandler(errorCode string) func(rw http.ResponseWriter, r *http.Request) { 716 func (p *ControllerRegistor) getErrorHandler(errorCode string) func(rw http.ResponseWriter, r *http.Request) {
...@@ -1013,30 +729,6 @@ func (p *ControllerRegistor) getErrorHandler(errorCode string) func(rw http.Resp ...@@ -1013,30 +729,6 @@ func (p *ControllerRegistor) getErrorHandler(errorCode string) func(rw http.Resp
1013 return handler 729 return handler
1014 } 730 }
1015 731
1016 // returns method name from request header or form field.
1017 // sometimes browsers can't create PUT and DELETE request.
1018 // set a form field "_method" instead.
1019 func (p *ControllerRegistor) getRunMethod(method string, context *beecontext.Context, router *controllerInfo) string {
1020 method = strings.ToLower(method)
1021 if method == "post" && strings.ToLower(context.Input.Query("_method")) == "put" {
1022 method = "put"
1023 }
1024 if method == "post" && strings.ToLower(context.Input.Query("_method")) == "delete" {
1025 method = "delete"
1026 }
1027 if router.hasMethod {
1028 if m, ok := router.methods[method]; ok {
1029 return m
1030 } else if m, ok = router.methods["*"]; ok {
1031 return m
1032 } else {
1033 return ""
1034 }
1035 } else {
1036 return strings.Title(method)
1037 }
1038 }
1039
1040 //responseWriter is a wrapper for the http.ResponseWriter 732 //responseWriter is a wrapper for the http.ResponseWriter
1041 //started set to true if response was written to then don't execute other handler 733 //started set to true if response was written to then don't execute other handler
1042 type responseWriter struct { 734 type responseWriter struct {
...@@ -1070,8 +762,18 @@ func (w *responseWriter) WriteHeader(code int) { ...@@ -1070,8 +762,18 @@ func (w *responseWriter) WriteHeader(code int) {
1070 func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { 762 func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
1071 hj, ok := w.writer.(http.Hijacker) 763 hj, ok := w.writer.(http.Hijacker)
1072 if !ok { 764 if !ok {
1073 println("supported?")
1074 return nil, nil, errors.New("webserver doesn't support hijacking") 765 return nil, nil, errors.New("webserver doesn't support hijacking")
1075 } 766 }
1076 return hj.Hijack() 767 return hj.Hijack()
1077 } 768 }
769
770 func tourl(params map[string]string) string {
771 if len(params) == 0 {
772 return ""
773 }
774 u := "?"
775 for k, v := range params {
776 u += k + "=" + v + "&"
777 }
778 return strings.TrimRight(u, "&")
779 }
......
...@@ -10,6 +10,7 @@ import ( ...@@ -10,6 +10,7 @@ import (
10 "net/http" 10 "net/http"
11 "net/http/httptest" 11 "net/http/httptest"
12 "testing" 12 "testing"
13
13 "github.com/astaxie/beego/context" 14 "github.com/astaxie/beego/context"
14 ) 15 )
15 16
...@@ -76,16 +77,17 @@ func TestUrlFor(t *testing.T) { ...@@ -76,16 +77,17 @@ func TestUrlFor(t *testing.T) {
76 handler.Add("/person/:last/:first", &TestController{}) 77 handler.Add("/person/:last/:first", &TestController{})
77 handler.AddAuto(&TestController{}) 78 handler.AddAuto(&TestController{})
78 if handler.UrlFor("TestController.List") != "/api/list" { 79 if handler.UrlFor("TestController.List") != "/api/list" {
80 Info(handler.UrlFor("TestController.List"))
79 t.Errorf("TestController.List must equal to /api/list") 81 t.Errorf("TestController.List must equal to /api/list")
80 } 82 }
81 if handler.UrlFor("TestController.Get", ":last", "xie", ":first", "asta") != "/person/xie/asta" { 83 if handler.UrlFor("TestController.Get", ":last", "xie", ":first", "asta") != "/person/xie/asta" {
82 t.Errorf("TestController.Get must equal to /person/xie/asta") 84 t.Errorf("TestController.Get must equal to /person/xie/asta")
83 } 85 }
84 if handler.UrlFor("TestController.Myext") != "/Test/Myext" { 86 if handler.UrlFor("TestController.Myext") != "/test/myext" {
85 t.Errorf("TestController.Myext must equal to /Test/Myext") 87 t.Errorf("TestController.Myext must equal to /test/myext")
86 } 88 }
87 if handler.UrlFor("TestController.GetUrl") != "/Test/GetUrl" { 89 if handler.UrlFor("TestController.GetUrl") != "/test/geturl" {
88 t.Errorf("TestController.GetUrl must equal to /Test/GetUrl") 90 t.Errorf("TestController.GetUrl must equal to /test/geturl")
89 } 91 }
90 } 92 }
91 93
......
...@@ -25,6 +25,26 @@ func NewTree() *Tree { ...@@ -25,6 +25,26 @@ func NewTree() *Tree {
25 } 25 }
26 } 26 }
27 27
28 // add Tree to the exist Tree
29 // prefix should has no params
30 func (t *Tree) AddTree(prefix string, tree *Tree) {
31 t.addtree(splitPath(prefix), tree)
32 }
33
34 func (t *Tree) addtree(segments []string, tree *Tree) {
35 if len(segments) == 0 {
36 panic("prefix should has path")
37 }
38 if len(segments) == 1 && segments[0] != "" {
39 t.fixrouters[segments[0]] = tree
40 return
41 }
42 seg := segments[0]
43 subTree := NewTree()
44 t.fixrouters[seg] = subTree
45 subTree.addtree(segments[1:], tree)
46 }
47
28 // call addseg function 48 // call addseg function
29 func (t *Tree) AddRouter(pattern string, runObject interface{}) { 49 func (t *Tree) AddRouter(pattern string, runObject interface{}) {
30 t.addseg(splitPath(pattern), runObject, nil, "") 50 t.addseg(splitPath(pattern), runObject, nil, "")
...@@ -83,22 +103,40 @@ func (t *Tree) match(segments []string, wildcardValues []string) (runObject inte ...@@ -83,22 +103,40 @@ func (t *Tree) match(segments []string, wildcardValues []string) (runObject inte
83 return t.leaf.runObject, pa 103 return t.leaf.runObject, pa
84 } 104 }
85 } 105 }
106 if t.wildcard != nil && t.wildcard.leaf != nil {
107 if ok, pa := t.wildcard.leaf.match(wildcardValues); ok {
108 return t.wildcard.leaf.runObject, pa
109 }
110 }
86 return nil, nil 111 return nil, nil
87 } 112 }
88 113
89 var seg string 114 seg, segs := segments[0], segments[1:]
90 seg, segments = segments[0], segments[1:]
91 115
92 subTree, ok := t.fixrouters[seg] 116 subTree, ok := t.fixrouters[seg]
93 if ok { 117 if ok {
94 runObject, params = subTree.match(segments, wildcardValues) 118 runObject, params = subTree.match(segs, wildcardValues)
119 } else if len(segs) == 0 { //.json .xml
120 if subindex := strings.LastIndex(seg, "."); subindex != -1 {
121 subTree, ok = t.fixrouters[seg[:subindex]]
122 if ok {
123 runObject, params = subTree.match(segs, wildcardValues)
124 if runObject != nil {
125 if params == nil {
126 params = make(map[string]string)
127 }
128 params[":ext"] = seg[subindex+1:]
129 return runObject, params
130 }
131 }
132 }
95 } 133 }
96 if runObject == nil && t.wildcard != nil { 134 if runObject == nil && t.wildcard != nil {
97 runObject, params = t.wildcard.match(segments, append(wildcardValues, seg)) 135 runObject, params = t.wildcard.match(segs, append(wildcardValues, seg))
98 } 136 }
99 if runObject == nil { 137 if runObject == nil {
100 if t.leaf != nil { 138 if t.leaf != nil {
101 if ok, pa := t.leaf.match(append(wildcardValues, seg)); ok { 139 if ok, pa := t.leaf.match(append(wildcardValues, segments...)); ok {
102 return t.leaf.runObject, pa 140 return t.leaf.runObject, pa
103 } 141 }
104 } 142 }
...@@ -122,7 +160,21 @@ func (leaf *leafInfo) match(wildcardValues []string) (ok bool, params map[string ...@@ -122,7 +160,21 @@ func (leaf *leafInfo) match(wildcardValues []string) (ok bool, params map[string
122 // has error 160 // has error
123 if len(wildcardValues) == 0 && len(leaf.wildcards) > 0 { 161 if len(wildcardValues) == 0 && len(leaf.wildcards) > 0 {
124 if utils.InSlice(":", leaf.wildcards) { 162 if utils.InSlice(":", leaf.wildcards) {
125 return true, nil 163 params = make(map[string]string)
164 j := 0
165 for _, v := range leaf.wildcards {
166 if v == ":" {
167 continue
168 }
169 params[v] = ""
170 j += 1
171 }
172 return true, params
173 }
174 if len(leaf.wildcards) == 1 && leaf.wildcards[0] == ":splat" {
175 params = make(map[string]string)
176 params[":splat"] = ""
177 return true, params
126 } 178 }
127 Error("bug of router") 179 Error("bug of router")
128 return false, nil 180 return false, nil
...@@ -155,10 +207,27 @@ func (leaf *leafInfo) match(wildcardValues []string) (ok bool, params map[string ...@@ -155,10 +207,27 @@ func (leaf *leafInfo) match(wildcardValues []string) (ok bool, params map[string
155 if v == ":" { 207 if v == ":" {
156 continue 208 continue
157 } 209 }
210 if v == "." {
211 lastone := wildcardValues[len(wildcardValues)-1]
212 strs := strings.SplitN(lastone, ".", 2)
213 if len(strs) == 2 {
214 params[":ext"] = strs[1]
215 } else {
216 params[":ext"] = ""
217 }
218 if len(wildcardValues[j:]) == 1 {
219 params[":path"] = strs[0]
220 } else {
221 params[":path"] = path.Join(wildcardValues[j:]...) + "/" + strs[0]
222 }
223 return true, params
224 }
158 params[v] = wildcardValues[j] 225 params[v] = wildcardValues[j]
159 j += 1 226 j += 1
160 } 227 }
161 if len(params) != len(wildcardValues) { 228 if len(params) != len(wildcardValues) {
229 Info(params)
230 Info(wildcardValues)
162 Error("bug of router") 231 Error("bug of router")
163 return false, nil 232 return false, nil
164 } 233 }
...@@ -193,7 +262,7 @@ func splitPath(key string) []string { ...@@ -193,7 +262,7 @@ func splitPath(key string) []string {
193 262
194 // "admin" -> false, nil, "" 263 // "admin" -> false, nil, ""
195 // ":id" -> true, [:id], "" 264 // ":id" -> true, [:id], ""
196 // "?:id" -> true, [: id], "" : meaning can empty 265 // "?:id" -> true, [: :id], "" : meaning can empty
197 // ":id:int" -> true, [:id], ([0-9]+) 266 // ":id:int" -> true, [:id], ([0-9]+)
198 // ":name:string" -> true, [:name], ([\w]+) 267 // ":name:string" -> true, [:name], ([\w]+)
199 // ":id([0-9]+)" -> true, [:id], ([0-9]+) 268 // ":id([0-9]+)" -> true, [:id], ([0-9]+)
......
...@@ -13,10 +13,16 @@ var routers []testinfo ...@@ -13,10 +13,16 @@ var routers []testinfo
13 func init() { 13 func init() {
14 routers = make([]testinfo, 0) 14 routers = make([]testinfo, 0)
15 routers = append(routers, testinfo{"/:id", "/123", map[string]string{":id": "123"}}) 15 routers = append(routers, testinfo{"/:id", "/123", map[string]string{":id": "123"}})
16 routers = append(routers, testinfo{"/hello/?:id", "/hello", map[string]string{":id": ""}})
16 routers = append(routers, testinfo{"/", "/", nil}) 17 routers = append(routers, testinfo{"/", "/", nil})
17 routers = append(routers, testinfo{"/customer/login", "/customer/login", nil}) 18 routers = append(routers, testinfo{"/customer/login", "/customer/login", nil})
19 routers = append(routers, testinfo{"/customer/login", "/customer/login.json", map[string]string{":ext": "json"}})
18 routers = append(routers, testinfo{"/*", "/customer/123", map[string]string{":splat": "customer/123"}}) 20 routers = append(routers, testinfo{"/*", "/customer/123", map[string]string{":splat": "customer/123"}})
21 routers = append(routers, testinfo{"/customer/*", "/customer", map[string]string{":splat": ""}})
22 routers = append(routers, testinfo{"/*", "/customer/2009/12/11", map[string]string{":splat": "customer/2009/12/11"}})
19 routers = append(routers, testinfo{"/*.*", "/nice/api.json", map[string]string{":path": "nice/api", ":ext": "json"}}) 23 routers = append(routers, testinfo{"/*.*", "/nice/api.json", map[string]string{":path": "nice/api", ":ext": "json"}})
24 routers = append(routers, testinfo{"/:name/*.*", "/nice/api.json", map[string]string{":name": "nice", ":path": "api", ":ext": "json"}})
25 routers = append(routers, testinfo{"/:name/test/*.*", "/nice/test/api.json", map[string]string{":name": "nice", ":path": "api", ":ext": "json"}})
20 routers = append(routers, testinfo{"/v1/shop/:id:int", "/v1/shop/123", map[string]string{":id": "123"}}) 26 routers = append(routers, testinfo{"/v1/shop/:id:int", "/v1/shop/123", map[string]string{":id": "123"}})
21 routers = append(routers, testinfo{"/v1/shop/:id/:name", "/v1/shop/123/nike", map[string]string{":id": "123", ":name": "nike"}}) 27 routers = append(routers, testinfo{"/v1/shop/:id/:name", "/v1/shop/123/nike", map[string]string{":id": "123", ":name": "nike"}})
22 routers = append(routers, testinfo{"/v1/shop/:id/account", "/v1/shop/123/account", map[string]string{":id": "123"}}) 28 routers = append(routers, testinfo{"/v1/shop/:id/account", "/v1/shop/123/account", map[string]string{":id": "123"}})
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!