add GetRoute for MVC Controllers. Give the chance to change rsome route's properties like Name (used for reverse routing in templates, optionally).

Former-commit-id: 5ee9b1f97ff3c98cd8e5faddd23a87bbafb8e2e0
This commit is contained in:
Gerasimos (Makis) Maropoulos 2017-12-30 22:04:26 +02:00
parent c8ade43b64
commit 617258890e
4 changed files with 67 additions and 17 deletions

View File

@ -16,12 +16,12 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene
> Iris uses the [vendor directory](https://docs.google.com/document/d/1Bz5-UB7g2uPBdOx-rw5t9MxJwkfpx90cqG9AFL0JAYo) feature, so you get truly reproducible builds, as this method guards against upstream renames and deletes.
**How to upgrade**: Open your command-line and execute this command: `go get -u github.com/kataras/iris` or let the automatic updater do that for you.
<!--
# Mo, 01 Jenuary 2018 | v10.0.0
Thanks to an amazing person, we have a new [logo](logo.svg) for Iris. The new logo was created by [@merry.dii
](https://www.instagram.com/merry.dii/).
We must thanks [Mrs. Diana](https://www.instagram.com/merry.dii/) for our awesome new [logo](https://iris-go.com/images/logo.svg)!
You can [contact](mailto:Kovalenkodiana8@gmail.com) her for any design-related enquiries or explore and send a direct message via [instagram](https://www.instagram.com/merry.dii/).
TODO:

View File

@ -19,6 +19,7 @@ It doesn't always contain the "best ways" but it does cover each important featu
- [Tutorial: DropzoneJS Uploader](tutorial/dropzonejs)
- [Tutorial: Caddy](tutorial/caddy)
- [Tutorial:Iris Go Framework + MongoDB](https://medium.com/go-language/iris-go-framework-mongodb-552e349eab9c)
### Structuring
Nothing stops you from using your favorite folder structure. Iris is a low level web framework, it has got MVC first-class support but it doesn't limit your folder structure, this is your choice.

View File

@ -25,6 +25,7 @@ type BaseController interface {
type shared interface {
Name() string
Router() router.Party
GetRoute(methodName string) *router.Route
Handle(httpMethod, path, funcName string, middleware ...context.Handler) *router.Route
}
@ -74,9 +75,10 @@ type ControllerActivator struct {
// i.e: if login-example/user/controller.go, the FullName is "user.Controller".
fullName string
// the methods names that is already binded to a handler,
// the BeginRequest, EndRequest and BeforeActivation are reserved by the internal implementation.
reservedMethods []string
// 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 also protects for duplicatations.
routes map[string]*router.Route
// the bindings that comes from the Engine and the controller's filled fields if any.
// Can be binded to the the new controller's fields and method that is fired
@ -116,10 +118,7 @@ func newControllerActivator(router router.Party, controller interface{}, depende
// are being appended to the slice at the `parseMethods`,
// if a new method is registered via `Handle` its function name
// is also appended to that slice.
//
// TODO: now that BaseController is totally optionally
// we have to check if BeginRequest and EndRequest should be here.
reservedMethods: whatReservedMethods(typ),
routes: whatReservedMethods(typ),
// CloneWithFieldsOf: include the manual fill-ed controller struct's fields to the dependencies.
dependencies: di.Values(dependencies).CloneWithFieldsOf(controller),
}
@ -127,13 +126,20 @@ func newControllerActivator(router router.Party, controller interface{}, depende
return c
}
func whatReservedMethods(typ reflect.Type) []string {
func whatReservedMethods(typ reflect.Type) map[string]*router.Route {
methods := []string{"BeforeActivation", "AfterActivation"}
// BeforeActivatior/AfterActivation are not routes but they are
// reserved names*
if isBaseController(typ) {
methods = append(methods, "BeginRequest", "EndRequest")
}
return methods
routes := make(map[string]*router.Route, len(methods))
for _, m := range methods {
routes[m] = &router.Route{}
}
return routes
}
// Dependencies returns the write and read access of the dependencies that are
@ -182,6 +188,24 @@ func (c *ControllerActivator) Router() router.Party {
return c.router
}
// GetRoute returns a 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 `Handle` too.
func (c *ControllerActivator) GetRoute(methodName string) *router.Route {
for name, route := range c.routes {
if name == methodName {
return route
}
}
return nil
}
// Singleton returns new if all incoming clients' requests
// have the same controller instance.
// This is done automatically by iris to reduce the creation
@ -196,8 +220,8 @@ func (c *ControllerActivator) Singleton() bool {
// checks if a method is already registered.
func (c *ControllerActivator) isReservedMethod(name string) bool {
for _, s := range c.reservedMethods {
if s == name {
for methodName := range c.routes {
if methodName == name {
return true
}
}
@ -291,7 +315,7 @@ func (c *ControllerActivator) Handle(method, path, funcName string, middleware .
// 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.reservedMethods = append(c.reservedMethods, funcName)
c.routes[funcName] = route
return route
}

View File

@ -34,13 +34,31 @@ type testControllerHandle struct {
reqField string
}
func (c *testControllerHandle) BeforeActivation(b BeforeActivation) { // BeforeActivation(t *mvc.TController) {
func (c *testControllerHandle) BeforeActivation(b BeforeActivation) {
b.Handle("GET", "/histatic", "HiStatic")
b.Handle("GET", "/hiservice", "HiService")
b.Handle("GET", "/hiparam/{ps:string}", "HiParamBy")
b.Handle("GET", "/hiparamempyinput/{ps:string}", "HiParamEmptyInputBy")
}
// test `GetRoute` for custom routes.
func (c *testControllerHandle) AfterActivation(a AfterActivation) {
// change automatic parser's route change name.
rget := a.GetRoute("Get")
if rget == nil {
panic("route from function name: 'Get' doesn't exist on `AfterActivation`")
}
rget.Name = "index_route"
// change a custom route's name.
r := a.GetRoute("HiStatic")
if r == nil {
panic("route from function name: HiStatic doesn't exist on `AfterActivation`")
}
// change the name here, and test if name changed in the handler.
r.Name = "hi_static_route"
}
func (c *testControllerHandle) BeginRequest(ctx iris.Context) {
c.reqField = ctx.URLParam("reqfield")
}
@ -48,10 +66,17 @@ func (c *testControllerHandle) BeginRequest(ctx iris.Context) {
func (c *testControllerHandle) EndRequest(ctx iris.Context) {}
func (c *testControllerHandle) Get() string {
if c.Ctx.GetCurrentRoute().Name() != "index_route" {
return "Get's route's name didn't change on AfterActivation"
}
return "index"
}
func (c *testControllerHandle) HiStatic() string {
if c.Ctx.GetCurrentRoute().Name() != "hi_static_route" {
return "HiStatic's route's name didn't change on AfterActivation"
}
return c.reqField
}