diff --git a/app.go b/app.go index 3344d1a5..6a56bb25 100644 --- a/app.go +++ b/app.go @@ -181,8 +181,8 @@ func (app *App) Any(rootpath string, f FilterFunc) *App { } // add router for http.Handler -func (app *App) Handler(rootpath string, h http.Handler) *App { - app.Handlers.Handler(rootpath, h) +func (app *App) Handler(rootpath string, h http.Handler, options ...interface{}) *App { + app.Handlers.Handler(rootpath, h, options...) return app } diff --git a/beego.go b/beego.go index 3631a7c6..e97f8a78 100644 --- a/beego.go +++ b/beego.go @@ -170,8 +170,8 @@ func Any(rootpath string, f FilterFunc) *App { } // register router for own Handler -func Handler(rootpath string, h http.Handler) *App { - BeeApp.Handler(rootpath, h) +func Handler(rootpath string, h http.Handler, options ...interface{}) *App { + BeeApp.Handler(rootpath, h, options...) return BeeApp } diff --git a/namespace.go b/namespace.go new file mode 100644 index 00000000..b9c88074 --- /dev/null +++ b/namespace.go @@ -0,0 +1,137 @@ +// 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 + +import ( + "net/http" + "strings" + + beecontext "github.com/astaxie/beego/context" +) + +type namespaceCond func(*beecontext.Context) bool + +type Namespace struct { + prefix string + condition namespaceCond + handlers *ControllerRegistor +} + +func NewNamespace(prefix string) *Namespace { + cr := NewControllerRegistor() + return &Namespace{ + prefix: prefix, + handlers: cr, + } +} + +func (n *Namespace) Cond(cond namespaceCond) *Namespace { + n.condition = cond + return n +} + +func (n *Namespace) Filter(action string, filter FilterFunc) *Namespace { + if action == "before" { + action = "BeforeRouter" + } else if action == "after" { + action = "FinishRouter" + } + n.handlers.AddFilter("*", action, filter) + return n +} + +func (n *Namespace) Router(rootpath string, c ControllerInterface, mappingMethods ...string) *Namespace { + n.handlers.Add(rootpath, c, mappingMethods...) + return n +} + +func (n *Namespace) AutoRouter(c ControllerInterface) *Namespace { + n.handlers.AddAuto(c) + return n +} + +func (n *Namespace) AutoPrefix(prefix string, c ControllerInterface) *Namespace { + n.handlers.AddAutoPrefix(prefix, c) + return n +} + +func (n *Namespace) Get(rootpath string, f FilterFunc) *Namespace { + n.handlers.Get(rootpath, f) + return n +} + +func (n *Namespace) Post(rootpath string, f FilterFunc) *Namespace { + n.handlers.Post(rootpath, f) + return n +} + +func (n *Namespace) Delete(rootpath string, f FilterFunc) *Namespace { + n.handlers.Delete(rootpath, f) + return n +} + +func (n *Namespace) Put(rootpath string, f FilterFunc) *Namespace { + n.handlers.Put(rootpath, f) + return n +} + +func (n *Namespace) Head(rootpath string, f FilterFunc) *Namespace { + n.handlers.Head(rootpath, f) + return n +} + +func (n *Namespace) Options(rootpath string, f FilterFunc) *Namespace { + n.handlers.Options(rootpath, f) + return n +} + +func (n *Namespace) Patch(rootpath string, f FilterFunc) *Namespace { + n.handlers.Patch(rootpath, f) + return n +} + +func (n *Namespace) Any(rootpath string, f FilterFunc) *Namespace { + n.handlers.Any(rootpath, f) + return n +} + +func (n *Namespace) Handler(rootpath string, h http.Handler) *Namespace { + n.handlers.Handler(rootpath, h) + return n +} + +func (n *Namespace) Namespace(ns *Namespace) *Namespace { + n.handlers.Handler(ns.prefix, ns, true) + return n +} + +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) + } + n.handlers.ServeHTTP(rw, r) +} + +func AddNamespace(nl ...*Namespace) { + for _, n := range nl { + Handler(n.prefix, n, true) + } +} diff --git a/namespace_test.go b/namespace_test.go new file mode 100644 index 00000000..48500004 --- /dev/null +++ b/namespace_test.go @@ -0,0 +1,137 @@ +// 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 + +import ( + "net/http" + "net/http/httptest" + "strconv" + "testing" + + "github.com/astaxie/beego/context" +) + +func TestNamespaceGet(t *testing.T) { + r, _ := http.NewRequest("GET", "/v1/user", nil) + w := httptest.NewRecorder() + + ns := NewNamespace("/v1") + ns.Get("/user", func(ctx *context.Context) { + ctx.Output.Body([]byte("v1_user")) + }) + ns.ServeHTTP(w, r) + if w.Body.String() != "v1_user" { + t.Errorf("TestNamespaceGet can't run, get the response is " + w.Body.String()) + } +} + +func TestNamespacePost(t *testing.T) { + r, _ := http.NewRequest("POST", "/v1/user/123", nil) + w := httptest.NewRecorder() + + ns := NewNamespace("/v1") + ns.Post("/user/:id", func(ctx *context.Context) { + ctx.Output.Body([]byte(ctx.Input.Param(":id"))) + }) + ns.ServeHTTP(w, r) + if w.Body.String() != "123" { + t.Errorf("TestNamespacePost can't run, get the response is " + w.Body.String()) + } +} + +func TestNamespaceNest(t *testing.T) { + r, _ := http.NewRequest("GET", "/v1/admin/order", nil) + w := httptest.NewRecorder() + + ns := NewNamespace("/v1") + ns.Namespace( + NewNamespace("/admin"). + Get("/order", func(ctx *context.Context) { + ctx.Output.Body([]byte("order")) + }), + ) + ns.ServeHTTP(w, r) + if w.Body.String() != "order" { + t.Errorf("TestNamespaceNest can't run, get the response is " + w.Body.String()) + } +} + +func TestNamespaceNestParam(t *testing.T) { + r, _ := http.NewRequest("GET", "/v1/admin/order/123", nil) + w := httptest.NewRecorder() + + ns := NewNamespace("/v1") + ns.Namespace( + NewNamespace("/admin"). + Get("/order/:id", func(ctx *context.Context) { + ctx.Output.Body([]byte(ctx.Input.Param(":id"))) + }), + ) + ns.ServeHTTP(w, r) + if w.Body.String() != "123" { + 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) { + r, _ := http.NewRequest("GET", "/v1/api/list", nil) + w := httptest.NewRecorder() + + ns := NewNamespace("/v1") + ns.Router("/api/list", &TestController{}, "*:List") + ns.ServeHTTP(w, r) + if w.Body.String() != "i am list" { + t.Errorf("TestNamespaceRouter can't run, get the response is " + w.Body.String()) + } +} + +func TestNamespaceAutoFunc(t *testing.T) { + r, _ := http.NewRequest("GET", "/v1/test/list", nil) + w := httptest.NewRecorder() + + ns := NewNamespace("/v1") + ns.AutoRouter(&TestController{}) + ns.ServeHTTP(w, r) + if w.Body.String() != "i am list" { + t.Errorf("user define func can't run") + } +} + +func TestNamespaceCond(t *testing.T) { + r, _ := http.NewRequest("GET", "/v1/test/list", nil) + w := httptest.NewRecorder() + + ns := NewNamespace("/v1") + ns.Cond(func(ctx *context.Context) bool { + if ctx.Input.Domain() == "beego.me" { + return true + } + return false + }). + AutoRouter(&TestController{}) + ns.ServeHTTP(w, r) + if w.Code != 405 { + t.Errorf("TestNamespaceCond can't run get the result " + strconv.Itoa(w.Code)) + } +} diff --git a/router.go b/router.go index 557bdc8b..3d6c1664 100644 --- a/router.go +++ b/router.go @@ -69,6 +69,7 @@ type controllerInfo struct { handler http.Handler runfunction FilterFunc routerType int + isPrefix bool } // ControllerRegistor containers registered router rules, controller handlers and filters. @@ -277,11 +278,16 @@ func (p *ControllerRegistor) AddMethod(method, pattern string, f FilterFunc) { } } -func (p *ControllerRegistor) Handler(pattern string, h http.Handler) { +func (p *ControllerRegistor) Handler(pattern string, h http.Handler, options ...interface{}) { paramnums, params, parts := p.splitRoute(pattern) route := &controllerInfo{} route.routerType = routerTypeHandler route.handler = h + if len(options) > 0 { + if v, ok := options[0].(bool); ok { + route.isPrefix = v + } + } if paramnums == 0 { route.pattern = pattern p.fixrouters = append(p.fixrouters, route) @@ -446,6 +452,7 @@ func (p *ControllerRegistor) AddFilter(pattern, action string, filter FilterFunc if err != nil { return err } + switch action { case "BeforeRouter": p.filters[BeforeRouter] = append(p.filters[BeforeRouter], mr) @@ -760,6 +767,14 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) break } } + if route.routerType == routerTypeHandler && route.isPrefix && + strings.HasPrefix(requestPath, route.pattern) { + + routerInfo = route + runrouter = route.controllerType + findrouter = true + break + } } }