Commit e00eab7f authored by astaxie's avatar astaxie

beego: change to tree

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) {
fmt.Fprintln(rw, "AdminHttpPort:", AdminHttpPort) fmt.Fprintln(rw, "AdminHttpPort:", AdminHttpPort)
case "router": case "router":
fmt.Fprintln(rw, "Print all router infomation:") fmt.Fprintln(rw, "Print all router infomation:")
for _, router := range BeeApp.Handlers.fixrouters { // @todo print routers
if router.routerType == routerTypeBeego {
if router.hasMethod {
fmt.Fprintln(rw, router.pattern, "----", router.methods, "----", router.controllerType.Name())
} else {
fmt.Fprintln(rw, router.pattern, "----", router.controllerType.Name())
}
} else if router.routerType == routerTypeRESTFul {
fmt.Fprintln(rw, router.pattern, "----", router.methods, "----", router.runfunction)
} else if router.routerType == routerTypeHandler {
fmt.Fprintln(rw, router.pattern, "----", router.handler)
}
}
for _, router := range BeeApp.Handlers.routers {
if router.routerType == routerTypeBeego {
if router.hasMethod {
fmt.Fprintln(rw, router.pattern, "----", router.methods, "----", router.controllerType.Name())
} else {
fmt.Fprintln(rw, router.pattern, "----", router.controllerType.Name())
}
} else if router.routerType == routerTypeRESTFul {
fmt.Fprintln(rw, router.pattern, "----", router.methods, "----", router.runfunction)
} else if router.routerType == routerTypeHandler {
fmt.Fprintln(rw, router.pattern, "----", router.handler)
}
}
if BeeApp.Handlers.enableAuto {
for controllerName, methodObj := range BeeApp.Handlers.autoRouter {
fmt.Fprintln(rw, controllerName, "----")
for methodName, obj := range methodObj {
fmt.Fprintln(rw, " ", methodName, "-----", obj.Name())
}
}
}
case "filter": case "filter":
fmt.Fprintln(rw, "Print all filter infomation:") fmt.Fprintln(rw, "Print all filter infomation:")
if BeeApp.Handlers.enableFilter { 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) {
fmt.Fprintln(rw, f.pattern, utils.GetFuncName(f.filterFunc)) fmt.Fprintln(rw, f.pattern, utils.GetFuncName(f.filterFunc))
} }
} }
fmt.Fprintln(rw, "AfterStatic:")
if bf, ok := BeeApp.Handlers.filters[AfterStatic]; ok {
for _, f := range bf {
fmt.Fprintln(rw, f.pattern, utils.GetFuncName(f.filterFunc))
}
}
fmt.Fprintln(rw, "BeforeExec:") fmt.Fprintln(rw, "BeforeExec:")
if bf, ok := BeeApp.Handlers.filters[BeforeExec]; ok { if bf, ok := BeeApp.Handlers.filters[BeforeExec]; ok {
for _, f := range bf { 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 {
// //
// regex router // regex router
// //
// beego.Router(“/api/:id([0-9]+)“, &controllers.RController{}) // beego.Router("/api/:id([0-9]+)", &controllers.RController{})
// //
// custom rules // custom rules
// beego.Router("/api/list",&RestController{},"*:ListFood") // 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
return BeeApp return BeeApp
} }
// Router add list from
// usage:
// beego.Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{})
// type BankAccount struct{
// beego.Controller
// }
//
// register the function
// func (b *BankAccount)Mapping(){
// b.Mapping("ShowAccount" , b.ShowAccount)
// b.Mapping("ModifyAccount", b.ModifyAccount)
//}
//
// //@router /account/:id [get]
// func (b *BankAccount) ShowAccount(){
// //logic
// }
//
//
// //@router /account/:id [post]
// func (b *BankAccount) ModifyAccount(){
// //logic
// }
//
// the comments @router url methodlist
// url support all the function Router's pattern
// methodlist [get post head put delete options *]
func Include(cList ...ControllerInterface) *App {
BeeApp.Handlers.Include(cList...)
return BeeApp
}
// RESTRouter adds a restful controller handler to BeeApp. // RESTRouter adds a restful controller handler to BeeApp.
// its' controller implements beego.ControllerInterface and // its' controller implements beego.ControllerInterface and
// defines a param "pattern/:objectId" to visit each resource. // 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 {
return BeeApp return BeeApp
} }
// [Deprecated] use InsertFilter.
// Filter adds a FilterFunc under pattern condition and named action.
// The actions contains BeforeRouter,AfterStatic,BeforeExec,AfterExec and FinishRouter.
func AddFilter(pattern, action string, filter FilterFunc) *App {
BeeApp.Handlers.AddFilter(pattern, action, filter)
return BeeApp
}
// InsertFilter adds a FilterFunc with pattern condition and action constant. // InsertFilter adds a FilterFunc with pattern condition and action constant.
// The pos means action constant including // The pos means action constant including
// beego.BeforeRouter, beego.AfterStatic, beego.BeforeExec, beego.AfterExec and beego.FinishRouter. // beego.BeforeRouter, beego.AfterStatic, beego.BeforeExec, beego.AfterExec and beego.FinishRouter.
......
...@@ -34,7 +34,8 @@ const ( ...@@ -34,7 +34,8 @@ const (
var ( var (
// custom error when user stop request handler manually. // custom error when user stop request handler manually.
USERSTOPRUN = errors.New("User stop run") USERSTOPRUN = errors.New("User stop run")
GlobalControllerRouter map[string]map[string]*Tree //pkgpath+controller:method:routertree
) )
// Controller defines some basic http request handler operations, such as // Controller defines some basic http request handler operations, such as
...@@ -55,6 +56,7 @@ type Controller struct { ...@@ -55,6 +56,7 @@ type Controller struct {
AppController interface{} AppController interface{}
EnableRender bool EnableRender bool
EnableXSRF bool EnableXSRF bool
Routers map[string]*Tree //method:routertree
} }
// ControllerInterface is an interface to uniform all controller handler. // ControllerInterface is an interface to uniform all controller handler.
...@@ -72,6 +74,8 @@ type ControllerInterface interface { ...@@ -72,6 +74,8 @@ type ControllerInterface interface {
Render() error Render() error
XsrfToken() string XsrfToken() string
CheckXsrfCookie() bool CheckXsrfCookie() bool
HandlerFunc(fn interface{})
URLMapping()
} }
// Init generates default values of controller operations. // 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
c.EnableRender = true c.EnableRender = true
c.EnableXSRF = true c.EnableXSRF = true
c.Data = ctx.Input.Data c.Data = ctx.Input.Data
c.Routers = make(map[string]*Tree)
} }
// Prepare runs after Init before request function execution. // Prepare runs after Init before request function execution.
...@@ -133,6 +138,32 @@ func (c *Controller) Options() { ...@@ -133,6 +138,32 @@ func (c *Controller) Options() {
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405) http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405)
} }
// call function fn
func (c *Controller) HandlerFunc(fn interface{}) {
if v, ok := fn.(func()); ok {
v()
}
}
// URLMapping register the internal Controller router.
func (c *Controller) URLMapping() {
}
func (c *Controller) Mapping(method, pattern string, fn func()) {
method = strings.ToLower(method)
if !utils.InSlice(method, HTTPMETHOD) && method != "*" {
Critical("add mapping method:" + method + " is a valid method")
return
}
if t, ok := c.Routers[method]; ok {
t.AddRouter(pattern, fn)
} else {
t = NewTree()
t.AddRouter(pattern, fn)
c.Routers[method] = t
}
}
// Render sends the response with rendered template bytes as text/html type. // Render sends the response with rendered template bytes as text/html type.
func (c *Controller) Render() error { func (c *Controller) Render() error {
if !c.EnableRender { if !c.EnableRender {
...@@ -295,7 +326,6 @@ func (c *Controller) ServeXml() { ...@@ -295,7 +326,6 @@ func (c *Controller) ServeXml() {
} }
// ServeFormatted serve Xml OR Json, depending on the value of the Accept header // ServeFormatted serve Xml OR Json, depending on the value of the Accept header
func (c *Controller) ServeFormatted() { func (c *Controller) ServeFormatted() {
accept := c.Ctx.Input.Header("Accept") accept := c.Ctx.Input.Header("Accept")
switch accept { switch accept {
......
...@@ -6,51 +6,24 @@ ...@@ -6,51 +6,24 @@
package beego package beego
import "regexp"
// FilterRouter defines filter operation before controller handler execution. // FilterRouter defines filter operation before controller handler execution.
// it can match patterned url and do filter function when action arrives. // it can match patterned url and do filter function when action arrives.
type FilterRouter struct { type FilterRouter struct {
pattern string filterFunc FilterFunc
regex *regexp.Regexp tree *Tree
filterFunc FilterFunc pattern string
hasregex bool
params map[int]string
parseParams map[string]string
} }
// ValidRouter check current request is valid for this filter. // ValidRouter check current request is valid for this filter.
// if matched, returns parsed params in this request by defined filter router pattern. // if matched, returns parsed params in this request by defined filter router pattern.
func (mr *FilterRouter) ValidRouter(router string) (bool, map[string]string) { func (f *FilterRouter) ValidRouter(router string) (bool, map[string]string) {
if mr.pattern == "" { isok, params := f.tree.Match(router)
return true, nil if isok == nil {
} return false, nil
if mr.pattern == "*" {
return true, nil
}
if router == mr.pattern {
return true, nil
} }
//pattern /admin router /admin/ match if isok, ok := isok.(bool); ok {
//pattern /admin/ router /admin don't match, because url will 301 in router return isok, params
if n := len(router); n > 1 && router[n-1] == '/' && router[:n-2] == mr.pattern { } else {
return true, nil return false, nil
}
if mr.hasregex {
if !mr.regex.MatchString(router) {
return false, nil
}
matches := mr.regex.FindStringSubmatch(router)
if len(matches) > 0 {
if len(matches[0]) == len(router) {
params := make(map[string]string)
for i, match := range matches[1:] {
params[mr.params[i]] = match
}
return true, params
}
}
} }
return false, nil
} }
...@@ -22,7 +22,7 @@ func TestFilter(t *testing.T) { ...@@ -22,7 +22,7 @@ func TestFilter(t *testing.T) {
r, _ := http.NewRequest("GET", "/person/asta/Xie", nil) r, _ := http.NewRequest("GET", "/person/asta/Xie", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
handler := NewControllerRegistor() handler := NewControllerRegistor()
handler.AddFilter("/person/:last/:first", "AfterStatic", FilterUser) handler.InsertFilter("/person/:last/:first", BeforeRouter, FilterUser)
handler.Add("/person/:last/:first", &TestController{}) handler.Add("/person/:last/:first", &TestController{})
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)
if w.Body.String() != "i am astaXie" { 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) {
r, _ := http.NewRequest("GET", "/admin/", nil) r, _ := http.NewRequest("GET", "/admin/", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
handler := NewControllerRegistor() handler := NewControllerRegistor()
handler.AddFilter("/admin/:all", "AfterStatic", FilterAdminUser) handler.InsertFilter("/admin/?:all", BeforeRouter, FilterAdminUser)
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)
if w.Body.String() != "i am admin" { if w.Body.String() != "i am admin" {
t.Errorf("filter /admin/ can't run") 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) {
r, _ := http.NewRequest("GET", "/admin/astaxie", nil) r, _ := http.NewRequest("GET", "/admin/astaxie", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
handler := NewControllerRegistor() handler := NewControllerRegistor()
handler.AddFilter("/admin/:all", "AfterStatic", FilterAdminUser) handler.InsertFilter("/admin/:all", BeforeRouter, FilterAdminUser)
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)
if w.Body.String() != "i am admin" { if w.Body.String() != "i am admin" {
t.Errorf("filter /admin/astaxie can't run") t.Errorf("filter /admin/astaxie can't run")
......
...@@ -7,18 +7,17 @@ package beego ...@@ -7,18 +7,17 @@ package beego
import ( import (
"net/http" "net/http"
"strings"
beecontext "github.com/astaxie/beego/context" beecontext "github.com/astaxie/beego/context"
"github.com/astaxie/beego/middleware"
) )
type namespaceCond func(*beecontext.Context) bool type namespaceCond func(*beecontext.Context) bool
// Namespace is store all the info // Namespace is store all the info
type Namespace struct { type Namespace struct {
prefix string prefix string
condition namespaceCond handlers *ControllerRegistor
handlers *ControllerRegistor
} }
// get new Namespace // get new Namespace
...@@ -39,8 +38,23 @@ func NewNamespace(prefix string) *Namespace { ...@@ -39,8 +38,23 @@ func NewNamespace(prefix string) *Namespace {
// } // }
// return false // return false
// }) // })
// Cond as the first filter
func (n *Namespace) Cond(cond namespaceCond) *Namespace { func (n *Namespace) Cond(cond namespaceCond) *Namespace {
n.condition = cond fn := func(ctx *beecontext.Context) {
if !cond(ctx) {
middleware.Exception("405", ctx.ResponseWriter, ctx.Request, "Method not allowed")
}
}
if v, ok := n.handlers.filters[BeforeRouter]; ok {
mr := new(FilterRouter)
mr.tree = NewTree()
mr.pattern = "*"
mr.filterFunc = fn
mr.tree.AddRouter("*", true)
n.handlers.filters[BeforeRouter] = append([]*FilterRouter{mr}, v...)
} else {
n.handlers.InsertFilter("*", BeforeRouter, fn)
}
return n return n
} }
...@@ -55,12 +69,13 @@ func (n *Namespace) Cond(cond namespaceCond) *Namespace { ...@@ -55,12 +69,13 @@ func (n *Namespace) Cond(cond namespaceCond) *Namespace {
// } // }
// }) // })
func (n *Namespace) Filter(action string, filter FilterFunc) *Namespace { func (n *Namespace) Filter(action string, filter FilterFunc) *Namespace {
var a int
if action == "before" { if action == "before" {
action = "BeforeRouter" a = BeforeRouter
} else if action == "after" { } else if action == "after" {
action = "FinishRouter" a = FinishRouter
} }
n.handlers.AddFilter("*", action, filter) n.handlers.InsertFilter("*", a, filter)
return n return n
} }
...@@ -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 {
//) //)
func (n *Namespace) Namespace(ns ...*Namespace) *Namespace { func (n *Namespace) Namespace(ns ...*Namespace) *Namespace {
for _, ni := range ns { for _, ni := range ns {
n.handlers.Handler(ni.prefix, ni, true) n.handlers.routers.AddTree(ni.prefix, ni.handlers.routers)
if n.handlers.enableFilter {
for pos, filterList := range ni.handlers.filters {
for _, mr := range filterList {
t := NewTree()
t.AddTree(ni.prefix, mr.tree)
mr.tree = t
n.handlers.insertFilterRouter(pos, mr)
}
}
}
} }
return n return n
} }
// Namespace implement the http.Handler
func (n *Namespace) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
//trim the preifix from URL.Path
r.URL.Path = strings.TrimPrefix(r.URL.Path, n.prefix)
// init context
context := &beecontext.Context{
ResponseWriter: rw,
Request: r,
Input: beecontext.NewInput(r),
Output: beecontext.NewOutput(),
}
context.Output.Context = context
context.Output.EnableGzip = EnableGzip
if context.Input.IsWebsocket() {
context.ResponseWriter = rw
}
if n.condition != nil && !n.condition(context) {
http.Error(rw, "Method Not Allowed", 405)
return
}
n.handlers.ServeHTTP(rw, r)
}
// register Namespace into beego.Handler // register Namespace into beego.Handler
// support multi Namespace // support multi Namespace
func AddNamespace(nl ...*Namespace) { func AddNamespace(nl ...*Namespace) {
for _, n := range nl { for _, n := range nl {
Handler(n.prefix, n, true) BeeApp.Handlers.routers.AddTree(n.prefix, n.handlers.routers)
if n.handlers.enableFilter {
for pos, filterList := range n.handlers.filters {
for _, mr := range filterList {
t := NewTree()
t.AddTree(n.prefix, mr.tree)
mr.tree = t
BeeApp.Handlers.insertFilterRouter(pos, mr)
}
}
}
} }
} }
...@@ -23,7 +23,8 @@ func TestNamespaceGet(t *testing.T) { ...@@ -23,7 +23,8 @@ func TestNamespaceGet(t *testing.T) {
ns.Get("/user", func(ctx *context.Context) { ns.Get("/user", func(ctx *context.Context) {
ctx.Output.Body([]byte("v1_user")) ctx.Output.Body([]byte("v1_user"))
}) })
ns.ServeHTTP(w, r) AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Body.String() != "v1_user" { if w.Body.String() != "v1_user" {
t.Errorf("TestNamespaceGet can't run, get the response is " + w.Body.String()) t.Errorf("TestNamespaceGet can't run, get the response is " + w.Body.String())
} }
...@@ -37,7 +38,8 @@ func TestNamespacePost(t *testing.T) { ...@@ -37,7 +38,8 @@ func TestNamespacePost(t *testing.T) {
ns.Post("/user/:id", func(ctx *context.Context) { ns.Post("/user/:id", func(ctx *context.Context) {
ctx.Output.Body([]byte(ctx.Input.Param(":id"))) ctx.Output.Body([]byte(ctx.Input.Param(":id")))
}) })
ns.ServeHTTP(w, r) AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Body.String() != "123" { if w.Body.String() != "123" {
t.Errorf("TestNamespacePost can't run, get the response is " + w.Body.String()) t.Errorf("TestNamespacePost can't run, get the response is " + w.Body.String())
} }
...@@ -54,7 +56,8 @@ func TestNamespaceNest(t *testing.T) { ...@@ -54,7 +56,8 @@ func TestNamespaceNest(t *testing.T) {
ctx.Output.Body([]byte("order")) ctx.Output.Body([]byte("order"))
}), }),
) )
ns.ServeHTTP(w, r) AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Body.String() != "order" { if w.Body.String() != "order" {
t.Errorf("TestNamespaceNest can't run, get the response is " + w.Body.String()) t.Errorf("TestNamespaceNest can't run, get the response is " + w.Body.String())
} }
...@@ -71,36 +74,21 @@ func TestNamespaceNestParam(t *testing.T) { ...@@ -71,36 +74,21 @@ func TestNamespaceNestParam(t *testing.T) {
ctx.Output.Body([]byte(ctx.Input.Param(":id"))) ctx.Output.Body([]byte(ctx.Input.Param(":id")))
}), }),
) )
ns.ServeHTTP(w, r) AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Body.String() != "123" { if w.Body.String() != "123" {
t.Errorf("TestNamespaceNestParam can't run, get the response is " + w.Body.String()) t.Errorf("TestNamespaceNestParam can't run, get the response is " + w.Body.String())
} }
} }
func TestNamespaceFilter(t *testing.T) {
r, _ := http.NewRequest("GET", "/v1/user/123", nil)
w := httptest.NewRecorder()
ns := NewNamespace("/v1")
ns.Filter("before", func(ctx *context.Context) {
ctx.Output.Body([]byte("this is Filter"))
}).
Get("/user/:id", func(ctx *context.Context) {
ctx.Output.Body([]byte(ctx.Input.Param(":id")))
})
ns.ServeHTTP(w, r)
if w.Body.String() != "this is Filter" {
t.Errorf("TestNamespaceFilter can't run, get the response is " + w.Body.String())
}
}
func TestNamespaceRouter(t *testing.T) { func TestNamespaceRouter(t *testing.T) {
r, _ := http.NewRequest("GET", "/v1/api/list", nil) r, _ := http.NewRequest("GET", "/v1/api/list", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
ns := NewNamespace("/v1") ns := NewNamespace("/v1")
ns.Router("/api/list", &TestController{}, "*:List") ns.Router("/api/list", &TestController{}, "*:List")
ns.ServeHTTP(w, r) AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Body.String() != "i am list" { if w.Body.String() != "i am list" {
t.Errorf("TestNamespaceRouter can't run, get the response is " + w.Body.String()) t.Errorf("TestNamespaceRouter can't run, get the response is " + w.Body.String())
} }
...@@ -112,17 +100,36 @@ func TestNamespaceAutoFunc(t *testing.T) { ...@@ -112,17 +100,36 @@ func TestNamespaceAutoFunc(t *testing.T) {
ns := NewNamespace("/v1") ns := NewNamespace("/v1")
ns.AutoRouter(&TestController{}) ns.AutoRouter(&TestController{})
ns.ServeHTTP(w, r) AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Body.String() != "i am list" { if w.Body.String() != "i am list" {
t.Errorf("user define func can't run") t.Errorf("user define func can't run")
} }
} }
func TestNamespaceCond(t *testing.T) { func TestNamespaceFilter(t *testing.T) {
r, _ := http.NewRequest("GET", "/v1/test/list", nil) r, _ := http.NewRequest("GET", "/v1/user/123", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
ns := NewNamespace("/v1") ns := NewNamespace("/v1")
ns.Filter("before", func(ctx *context.Context) {
ctx.Output.Body([]byte("this is Filter"))
}).
Get("/user/:id", func(ctx *context.Context) {
ctx.Output.Body([]byte(ctx.Input.Param(":id")))
})
AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Body.String() != "this is Filter" {
t.Errorf("TestNamespaceFilter can't run, get the response is " + w.Body.String())
}
}
func TestNamespaceCond(t *testing.T) {
r, _ := http.NewRequest("GET", "/v2/test/list", nil)
w := httptest.NewRecorder()
ns := NewNamespace("/v2")
ns.Cond(func(ctx *context.Context) bool { ns.Cond(func(ctx *context.Context) bool {
if ctx.Input.Domain() == "beego.me" { if ctx.Input.Domain() == "beego.me" {
return true return true
...@@ -130,7 +137,8 @@ func TestNamespaceCond(t *testing.T) { ...@@ -130,7 +137,8 @@ func TestNamespaceCond(t *testing.T) {
return false return false
}). }).
AutoRouter(&TestController{}) AutoRouter(&TestController{})
ns.ServeHTTP(w, r) AddNamespace(ns)
BeeApp.Handlers.ServeHTTP(w, r)
if w.Code != 405 { if w.Code != 405 {
t.Errorf("TestNamespaceCond can't run get the result " + strconv.Itoa(w.Code)) t.Errorf("TestNamespaceCond can't run get the result " + strconv.Itoa(w.Code))
} }
......
// Beego (http://beego.me/)
// @description beego is an open-source, high-performance web framework for the Go programming language.
// @link http://github.com/astaxie/beego for the canonical source repository
// @license http://github.com/astaxie/beego/blob/master/LICENSE
// @authors astaxie
package beego
This diff is collapsed.
...@@ -10,6 +10,7 @@ import ( ...@@ -10,6 +10,7 @@ import (
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
"github.com/astaxie/beego/context" "github.com/astaxie/beego/context"
) )
...@@ -76,16 +77,17 @@ func TestUrlFor(t *testing.T) { ...@@ -76,16 +77,17 @@ func TestUrlFor(t *testing.T) {
handler.Add("/person/:last/:first", &TestController{}) handler.Add("/person/:last/:first", &TestController{})
handler.AddAuto(&TestController{}) handler.AddAuto(&TestController{})
if handler.UrlFor("TestController.List") != "/api/list" { if handler.UrlFor("TestController.List") != "/api/list" {
Info(handler.UrlFor("TestController.List"))
t.Errorf("TestController.List must equal to /api/list") t.Errorf("TestController.List must equal to /api/list")
} }
if handler.UrlFor("TestController.Get", ":last", "xie", ":first", "asta") != "/person/xie/asta" { if handler.UrlFor("TestController.Get", ":last", "xie", ":first", "asta") != "/person/xie/asta" {
t.Errorf("TestController.Get must equal to /person/xie/asta") t.Errorf("TestController.Get must equal to /person/xie/asta")
} }
if handler.UrlFor("TestController.Myext") != "/Test/Myext" { if handler.UrlFor("TestController.Myext") != "/test/myext" {
t.Errorf("TestController.Myext must equal to /Test/Myext") t.Errorf("TestController.Myext must equal to /test/myext")
} }
if handler.UrlFor("TestController.GetUrl") != "/Test/GetUrl" { if handler.UrlFor("TestController.GetUrl") != "/test/geturl" {
t.Errorf("TestController.GetUrl must equal to /Test/GetUrl") t.Errorf("TestController.GetUrl must equal to /test/geturl")
} }
} }
......
...@@ -25,6 +25,26 @@ func NewTree() *Tree { ...@@ -25,6 +25,26 @@ func NewTree() *Tree {
} }
} }
// add Tree to the exist Tree
// prefix should has no params
func (t *Tree) AddTree(prefix string, tree *Tree) {
t.addtree(splitPath(prefix), tree)
}
func (t *Tree) addtree(segments []string, tree *Tree) {
if len(segments) == 0 {
panic("prefix should has path")
}
if len(segments) == 1 && segments[0] != "" {
t.fixrouters[segments[0]] = tree
return
}
seg := segments[0]
subTree := NewTree()
t.fixrouters[seg] = subTree
subTree.addtree(segments[1:], tree)
}
// call addseg function // call addseg function
func (t *Tree) AddRouter(pattern string, runObject interface{}) { func (t *Tree) AddRouter(pattern string, runObject interface{}) {
t.addseg(splitPath(pattern), runObject, nil, "") 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
return t.leaf.runObject, pa return t.leaf.runObject, pa
} }
} }
if t.wildcard != nil && t.wildcard.leaf != nil {
if ok, pa := t.wildcard.leaf.match(wildcardValues); ok {
return t.wildcard.leaf.runObject, pa
}
}
return nil, nil return nil, nil
} }
var seg string seg, segs := segments[0], segments[1:]
seg, segments = segments[0], segments[1:]
subTree, ok := t.fixrouters[seg] subTree, ok := t.fixrouters[seg]
if ok { if ok {
runObject, params = subTree.match(segments, wildcardValues) runObject, params = subTree.match(segs, wildcardValues)
} else if len(segs) == 0 { //.json .xml
if subindex := strings.LastIndex(seg, "."); subindex != -1 {
subTree, ok = t.fixrouters[seg[:subindex]]
if ok {
runObject, params = subTree.match(segs, wildcardValues)
if runObject != nil {
if params == nil {
params = make(map[string]string)
}
params[":ext"] = seg[subindex+1:]
return runObject, params
}
}
}
} }
if runObject == nil && t.wildcard != nil { if runObject == nil && t.wildcard != nil {
runObject, params = t.wildcard.match(segments, append(wildcardValues, seg)) runObject, params = t.wildcard.match(segs, append(wildcardValues, seg))
} }
if runObject == nil { if runObject == nil {
if t.leaf != nil { if t.leaf != nil {
if ok, pa := t.leaf.match(append(wildcardValues, seg)); ok { if ok, pa := t.leaf.match(append(wildcardValues, segments...)); ok {
return t.leaf.runObject, pa return t.leaf.runObject, pa
} }
} }
...@@ -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
// has error // has error
if len(wildcardValues) == 0 && len(leaf.wildcards) > 0 { if len(wildcardValues) == 0 && len(leaf.wildcards) > 0 {
if utils.InSlice(":", leaf.wildcards) { if utils.InSlice(":", leaf.wildcards) {
return true, nil params = make(map[string]string)
j := 0
for _, v := range leaf.wildcards {
if v == ":" {
continue
}
params[v] = ""
j += 1
}
return true, params
}
if len(leaf.wildcards) == 1 && leaf.wildcards[0] == ":splat" {
params = make(map[string]string)
params[":splat"] = ""
return true, params
} }
Error("bug of router") Error("bug of router")
return false, nil 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
if v == ":" { if v == ":" {
continue continue
} }
if v == "." {
lastone := wildcardValues[len(wildcardValues)-1]
strs := strings.SplitN(lastone, ".", 2)
if len(strs) == 2 {
params[":ext"] = strs[1]
} else {
params[":ext"] = ""
}
if len(wildcardValues[j:]) == 1 {
params[":path"] = strs[0]
} else {
params[":path"] = path.Join(wildcardValues[j:]...) + "/" + strs[0]
}
return true, params
}
params[v] = wildcardValues[j] params[v] = wildcardValues[j]
j += 1 j += 1
} }
if len(params) != len(wildcardValues) { if len(params) != len(wildcardValues) {
Info(params)
Info(wildcardValues)
Error("bug of router") Error("bug of router")
return false, nil return false, nil
} }
...@@ -193,7 +262,7 @@ func splitPath(key string) []string { ...@@ -193,7 +262,7 @@ func splitPath(key string) []string {
// "admin" -> false, nil, "" // "admin" -> false, nil, ""
// ":id" -> true, [:id], "" // ":id" -> true, [:id], ""
// "?:id" -> true, [: id], "" : meaning can empty // "?:id" -> true, [: :id], "" : meaning can empty
// ":id:int" -> true, [:id], ([0-9]+) // ":id:int" -> true, [:id], ([0-9]+)
// ":name:string" -> true, [:name], ([\w]+) // ":name:string" -> true, [:name], ([\w]+)
// ":id([0-9]+)" -> true, [:id], ([0-9]+) // ":id([0-9]+)" -> true, [:id], ([0-9]+)
......
...@@ -13,10 +13,16 @@ var routers []testinfo ...@@ -13,10 +13,16 @@ var routers []testinfo
func init() { func init() {
routers = make([]testinfo, 0) routers = make([]testinfo, 0)
routers = append(routers, testinfo{"/:id", "/123", map[string]string{":id": "123"}}) routers = append(routers, testinfo{"/:id", "/123", map[string]string{":id": "123"}})
routers = append(routers, testinfo{"/hello/?:id", "/hello", map[string]string{":id": ""}})
routers = append(routers, testinfo{"/", "/", nil}) routers = append(routers, testinfo{"/", "/", nil})
routers = append(routers, testinfo{"/customer/login", "/customer/login", nil}) routers = append(routers, testinfo{"/customer/login", "/customer/login", nil})
routers = append(routers, testinfo{"/customer/login", "/customer/login.json", map[string]string{":ext": "json"}})
routers = append(routers, testinfo{"/*", "/customer/123", map[string]string{":splat": "customer/123"}}) routers = append(routers, testinfo{"/*", "/customer/123", map[string]string{":splat": "customer/123"}})
routers = append(routers, testinfo{"/customer/*", "/customer", map[string]string{":splat": ""}})
routers = append(routers, testinfo{"/*", "/customer/2009/12/11", map[string]string{":splat": "customer/2009/12/11"}})
routers = append(routers, testinfo{"/*.*", "/nice/api.json", map[string]string{":path": "nice/api", ":ext": "json"}}) routers = append(routers, testinfo{"/*.*", "/nice/api.json", map[string]string{":path": "nice/api", ":ext": "json"}})
routers = append(routers, testinfo{"/:name/*.*", "/nice/api.json", map[string]string{":name": "nice", ":path": "api", ":ext": "json"}})
routers = append(routers, testinfo{"/:name/test/*.*", "/nice/test/api.json", map[string]string{":name": "nice", ":path": "api", ":ext": "json"}})
routers = append(routers, testinfo{"/v1/shop/:id:int", "/v1/shop/123", map[string]string{":id": "123"}}) routers = append(routers, testinfo{"/v1/shop/:id:int", "/v1/shop/123", map[string]string{":id": "123"}})
routers = append(routers, testinfo{"/v1/shop/:id/:name", "/v1/shop/123/nike", map[string]string{":id": "123", ":name": "nike"}}) routers = append(routers, testinfo{"/v1/shop/:id/:name", "/v1/shop/123/nike", map[string]string{":id": "123", ":name": "nike"}})
routers = append(routers, testinfo{"/v1/shop/:id/account", "/v1/shop/123/account", map[string]string{":id": "123"}}) routers = append(routers, testinfo{"/v1/shop/:id/account", "/v1/shop/123/account", map[string]string{":id": "123"}})
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment