add HandleMany to controller's Before/AfterActivation feature as requested at: https://github.com/kataras/iris/issues/1292

Former-commit-id: c021f49522a214ddc3978a1c8f5e8d32f029eb2d
This commit is contained in:
Gerasimos (Makis) Maropoulos 2019-07-11 15:07:39 +03:00
parent 33dfb42d73
commit f8d19b3ed2
3 changed files with 73 additions and 22 deletions

View File

@ -36,6 +36,7 @@ func basicMVC(app *mvc.Application) {
// GET: http://localhost:8080/basic // GET: http://localhost:8080/basic
// GET: http://localhost:8080/basic/custom // GET: http://localhost:8080/basic/custom
// GET: http://localhost:8080/basic/custom2
app.Handle(new(basicController)) app.Handle(new(basicController))
// All dependencies of the parent *mvc.Application // All dependencies of the parent *mvc.Application
@ -73,7 +74,7 @@ type basicController struct {
} }
func (c *basicController) BeforeActivation(b mvc.BeforeActivation) { 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) { func (c *basicController) AfterActivation(a mvc.AfterActivation) {

View File

@ -26,7 +26,9 @@ type shared interface {
Name() string Name() string
Router() router.Party Router() router.Party
GetRoute(methodName string) *router.Route GetRoute(methodName string) *router.Route
GetRoutes(methodName string) []*router.Route
Handle(httpMethod, path, funcName string, middleware ...context.Handler) *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 // 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. // 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 // 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. // `GetRoute/GetRoutes(functionName)`.
routes map[string]*router.Route routes map[string][]*router.Route
// the bindings that comes from the Engine and the controller's filled fields if any. // 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 // 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 return c
} }
func whatReservedMethods(typ reflect.Type) map[string]*router.Route { func whatReservedMethods(typ reflect.Type) map[string][]*router.Route {
methods := []string{"BeforeActivation", "AfterActivation"} methods := []string{"BeforeActivation", "AfterActivation"}
// BeforeActivatior/AfterActivation are not routes but they are // BeforeActivatior/AfterActivation are not routes but they are
// reserved names* // reserved names*
@ -140,9 +142,9 @@ func whatReservedMethods(typ reflect.Type) map[string]*router.Route {
methods = append(methods, "BeginRequest", "EndRequest") 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 { for _, m := range methods {
routes[m] = &router.Route{} routes[m] = []*router.Route{}
} }
return routes return routes
@ -199,7 +201,25 @@ func (c *ControllerActivator) Router() router.Party {
return c.router 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 // 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. // 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 // 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. // A check for `nil` is necessary for unregistered methods.
// //
// See `Handle` too. // See `Handle` too.
func (c *ControllerActivator) GetRoute(methodName string) *router.Route { func (c *ControllerActivator) GetRoutes(methodName string) []*router.Route {
for name, route := range c.routes { for name, routes := range c.routes {
if name == methodName { if name == methodName {
return route return routes
} }
} }
return nil return nil
@ -274,10 +294,33 @@ func (c *ControllerActivator) parseMethod(m reflect.Method) {
// and a function name that belongs to the controller, it accepts // and a function name that belongs to the controller, it accepts
// a forth, optionally, variadic parameter which is the before handlers. // 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 // 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 { 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 == "" || if method == "" || path == "" || funcName == "" ||
c.isReservedMethod(funcName) { c.isReservedMethod(funcName) {
// isReservedMethod -> if it's already registered // isReservedMethod -> if it's already registered
@ -313,22 +356,30 @@ func (c *ControllerActivator) Handle(method, path, funcName string, middleware .
handler := c.handlerOf(m, funcDependencies) handler := c.handlerOf(m, funcDependencies)
// register the handler now. // register the handler now.
route := c.router.Handle(method, path, append(middleware, handler)...) routes := c.router.HandleMany(method, path, append(middleware, handler)...)
if route == nil { if routes == nil {
c.addErr(fmt.Errorf("MVC: unable to register a route for the path for '%s.%s'", c.fullName, funcName)) c.addErr(fmt.Errorf("MVC: unable to register a route for the path for '%s.%s'", c.fullName, funcName))
return nil return nil
} }
for _, r := range routes {
// change the main handler's name in order to respect the controller's and give // change the main handler's name in order to respect the controller's and give
// a proper debug message. // a proper debug message.
route.MainHandlerName = fmt.Sprintf("%s.%s", c.fullName, funcName) r.MainHandlerName = fmt.Sprintf("%s.%s", c.fullName, funcName)
}
// add this as a reserved method name in order to // add this as a reserved method name in order to
// be sure that the same func will not be registered again, // be sure that the same route
// even if a custom .Handle later on. // (method is allowed to be registered more than one on different routes - v11.2).
c.routes[funcName] = route
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{} var emptyIn = []reflect.Value{}

View File

@ -4,7 +4,6 @@ import (
"github.com/kataras/iris/context" "github.com/kataras/iris/context"
"github.com/kataras/neffos" "github.com/kataras/neffos"
"github.com/kataras/neffos/gobwas" "github.com/kataras/neffos/gobwas"
"github.com/kataras/neffos/gorilla" "github.com/kataras/neffos/gorilla"
"github.com/kataras/neffos/stackexchange/redis" "github.com/kataras/neffos/stackexchange/redis"