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/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) {

View File

@ -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{}

View File

@ -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"