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
...@@ -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!