From f8d19b3ed210d24a7caa48d35164d1279306a727 Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Thu, 11 Jul 2019 15:07:39 +0300 Subject: [PATCH] add HandleMany to controller's Before/AfterActivation feature as requested at: https://github.com/kataras/iris/issues/1292 Former-commit-id: c021f49522a214ddc3978a1c8f5e8d32f029eb2d --- _examples/mvc/basic/main.go | 3 +- mvc/controller.go | 91 +++++++++++++++++++++++++++++-------- websocket/websocket.go | 1 - 3 files changed, 73 insertions(+), 22 deletions(-) diff --git a/_examples/mvc/basic/main.go b/_examples/mvc/basic/main.go index c28b06af..daf6dcff 100644 --- a/_examples/mvc/basic/main.go +++ b/_examples/mvc/basic/main.go @@ -36,6 +36,7 @@ func basicMVC(app *mvc.Application) { // GET: http://localhost:8080/basic // GET: http://localhost:8080/basic/custom + // GET: http://localhost:8080/basic/custom2 app.Handle(new(basicController)) // All dependencies of the parent *mvc.Application @@ -73,7 +74,7 @@ type basicController struct { } func (c *basicController) BeforeActivation(b mvc.BeforeActivation) { - b.Handle("GET", "/custom", "Custom") + b.HandleMany("GET", "/custom /custom2", "Custom") } func (c *basicController) AfterActivation(a mvc.AfterActivation) { diff --git a/mvc/controller.go b/mvc/controller.go index ef3813a7..a372db9e 100644 --- a/mvc/controller.go +++ b/mvc/controller.go @@ -26,7 +26,9 @@ type shared interface { Name() string Router() router.Party GetRoute(methodName string) *router.Route + GetRoutes(methodName string) []*router.Route Handle(httpMethod, path, funcName string, middleware ...context.Handler) *router.Route + HandleMany(httpMethod, path, funcName string, middleware ...context.Handler) []*router.Route } // BeforeActivation is being used as the only one input argument of a @@ -77,8 +79,8 @@ type ControllerActivator struct { // the already-registered routes, key = the controller's function name. // End-devs can change some properties of the *Route on the `BeforeActivator` by using the - // `GetRoute(functionName)`. It's a shield against duplications as well. - routes map[string]*router.Route + // `GetRoute/GetRoutes(functionName)`. + routes map[string][]*router.Route // the bindings that comes from the Engine and the controller's filled fields if any. // Can be bind-ed to the the new controller's fields and method that is fired @@ -132,7 +134,7 @@ func newControllerActivator(router router.Party, controller interface{}, depende return c } -func whatReservedMethods(typ reflect.Type) map[string]*router.Route { +func whatReservedMethods(typ reflect.Type) map[string][]*router.Route { methods := []string{"BeforeActivation", "AfterActivation"} // BeforeActivatior/AfterActivation are not routes but they are // reserved names* @@ -140,9 +142,9 @@ func whatReservedMethods(typ reflect.Type) map[string]*router.Route { methods = append(methods, "BeginRequest", "EndRequest") } - routes := make(map[string]*router.Route, len(methods)) + routes := make(map[string][]*router.Route, len(methods)) for _, m := range methods { - routes[m] = &router.Route{} + routes[m] = []*router.Route{} } return routes @@ -199,7 +201,25 @@ func (c *ControllerActivator) Router() router.Party { return c.router } -// GetRoute returns a registered route based on the controller's method name. +// GetRoute returns the first registered route based on the controller's method name. +// It can be used to change the route's name, which is useful for reverse routing +// inside views. Custom routes can be registered with `Handle`, which returns the *Route. +// This method exists mostly for the automatic method parsing based on the known patterns +// inside a controller. +// +// A check for `nil` is necessary for unregistered methods. +// +// See `GetRoutes` and `Handle` too. +func (c *ControllerActivator) GetRoute(methodName string) *router.Route { + routes := c.GetRoutes(methodName) + if len(routes) > 0 { + return routes[0] + } + + return nil +} + +// GetRoutes returns one or more registered route based on the controller's method name. // It can be used to change the route's name, which is useful for reverse routing // inside views. Custom routes can be registered with `Handle`, which returns the *Route. // This method exists mostly for the automatic method parsing based on the known patterns @@ -208,10 +228,10 @@ func (c *ControllerActivator) Router() router.Party { // A check for `nil` is necessary for unregistered methods. // // See `Handle` too. -func (c *ControllerActivator) GetRoute(methodName string) *router.Route { - for name, route := range c.routes { +func (c *ControllerActivator) GetRoutes(methodName string) []*router.Route { + for name, routes := range c.routes { if name == methodName { - return route + return routes } } return nil @@ -274,10 +294,33 @@ func (c *ControllerActivator) parseMethod(m reflect.Method) { // and a function name that belongs to the controller, it accepts // a forth, optionally, variadic parameter which is the before handlers. // -// Just like `APIBuilder`, it returns the `*router.Route`, if failed +// Just like `Party#Handle`, it returns the `*router.Route`, if failed // then it logs the errors and it returns nil, you can check the errors -// programmatically by the `APIBuilder#GetReporter`. +// programmatically by the `Party#GetReporter`. func (c *ControllerActivator) Handle(method, path, funcName string, middleware ...context.Handler) *router.Route { + routes := c.HandleMany(method, path, funcName, middleware...) + if len(routes) == 0 { + return nil + } + + return routes[0] +} + +// HandleMany like `Handle` but can register more than one path and HTTP method routes +// separated by whitespace on the same controller's method. +// Keep note that if the controller's method input arguments are path parameters dependencies +// they should match with each of the given paths. +// +// Just like `Party#HandleMany`:, it returns the `[]*router.Routes`. +// Usage: +// func (*Controller) BeforeActivation(b mvc.BeforeActivation) { +// b.HandleMany("GET", "/path /path1" /path2", "HandlePath") +// } +func (c *ControllerActivator) HandleMany(method, path, funcName string, middleware ...context.Handler) []*router.Route { + return c.handleMany(method, path, funcName, true, middleware...) +} + +func (c *ControllerActivator) handleMany(method, path, funcName string, override bool, middleware ...context.Handler) []*router.Route { if method == "" || path == "" || funcName == "" || c.isReservedMethod(funcName) { // isReservedMethod -> if it's already registered @@ -313,22 +356,30 @@ func (c *ControllerActivator) Handle(method, path, funcName string, middleware . handler := c.handlerOf(m, funcDependencies) // register the handler now. - route := c.router.Handle(method, path, append(middleware, handler)...) - if route == nil { + routes := c.router.HandleMany(method, path, append(middleware, handler)...) + if routes == nil { c.addErr(fmt.Errorf("MVC: unable to register a route for the path for '%s.%s'", c.fullName, funcName)) return nil } - // change the main handler's name in order to respect the controller's and give - // a proper debug message. - route.MainHandlerName = fmt.Sprintf("%s.%s", c.fullName, funcName) + for _, r := range routes { + // change the main handler's name in order to respect the controller's and give + // a proper debug message. + r.MainHandlerName = fmt.Sprintf("%s.%s", c.fullName, funcName) + } // add this as a reserved method name in order to - // be sure that the same func will not be registered again, - // even if a custom .Handle later on. - c.routes[funcName] = route + // be sure that the same route + // (method is allowed to be registered more than one on different routes - v11.2). - return route + existingRoutes, exist := c.routes[funcName] + if override || !exist { + c.routes[funcName] = routes + } else { + c.routes[funcName] = append(existingRoutes, routes...) + } + + return routes } var emptyIn = []reflect.Value{} diff --git a/websocket/websocket.go b/websocket/websocket.go index e415b1b1..7be42b99 100644 --- a/websocket/websocket.go +++ b/websocket/websocket.go @@ -4,7 +4,6 @@ import ( "github.com/kataras/iris/context" "github.com/kataras/neffos" - "github.com/kataras/neffos/gobwas" "github.com/kataras/neffos/gorilla" "github.com/kataras/neffos/stackexchange/redis"