1
0
mirror of https://github.com/kataras/iris.git synced 2026-01-21 10:56:01 +00:00

MVC improvements: add HandleWebsocket that now registers events automatically based on the struct's methods(!) and fix a bug when more than one value of the same type is registered to a static field of a controller

Former-commit-id: e369d1426ac1a6b58314930a18362670317da3c1
This commit is contained in:
Gerasimos (Makis) Maropoulos
2019-07-09 12:16:19 +03:00
parent 85666da682
commit 450f20902d
18 changed files with 383 additions and 183 deletions

View File

@@ -87,8 +87,11 @@ type ControllerActivator struct {
errorHandler hero.ErrorHandler
// initialized on the first `Handle`.
// initialized on the first `Handle` or immediately when "servesWebsocket" is true.
injector *di.StructInjector
// true if this controller listens and serves to websocket events.
servesWebsocket bool
}
// NameOf returns the package name + the struct type's name,
@@ -145,6 +148,11 @@ func whatReservedMethods(typ reflect.Type) map[string]*router.Route {
return routes
}
func (c *ControllerActivator) markAsWebsocket() {
c.servesWebsocket = true
c.attachInjector()
}
// Dependencies returns the write and read access of the dependencies that are
// came from the parent MVC Application, with this you can customize
// the dependencies per controller, used at the `BeforeActivation`.
@@ -325,6 +333,21 @@ func (c *ControllerActivator) Handle(method, path, funcName string, middleware .
var emptyIn = []reflect.Value{}
func (c *ControllerActivator) attachInjector() {
if c.injector == nil {
c.injector = di.Struct(c.Value, c.dependencies...)
if !c.servesWebsocket {
golog.Debugf("MVC Controller [%s] [Scope=%s]", c.fullName, c.injector.Scope)
} else {
golog.Debugf("MVC Websocket Controller [%s]", c.fullName)
}
if c.injector.Has {
golog.Debugf("Dependencies:\n%s", c.injector.String())
}
}
}
func (c *ControllerActivator) handlerOf(m reflect.Method, funcDependencies []reflect.Value) context.Handler {
// Remember:
// The `Handle->handlerOf` can be called from `BeforeActivation` event
@@ -333,12 +356,7 @@ func (c *ControllerActivator) handlerOf(m reflect.Method, funcDependencies []ref
// To solve this we're doing a check on the FIRST `Handle`,
// if c.injector is nil, then set it with the current bindings,
// these bindings can change after, so first add dependencies and after register routes.
if c.injector == nil {
c.injector = di.Struct(c.Value, c.dependencies...)
if c.injector.Has {
golog.Debugf("MVC dependencies of '%s':\n%s", c.fullName, c.injector.String())
}
}
c.attachInjector()
// fmt.Printf("for %s | values: %s\n", funcName, funcDependencies)

View File

@@ -1,12 +1,14 @@
package mvc
import (
"reflect"
"strings"
"github.com/kataras/iris/context"
"github.com/kataras/iris/core/router"
"github.com/kataras/iris/hero"
"github.com/kataras/iris/hero/di"
"github.com/kataras/iris/websocket"
"github.com/kataras/golog"
)
@@ -172,6 +174,52 @@ Set the Logger's Level to "debug" to view the active dependencies per controller
//
// Examples at: https://github.com/kataras/iris/tree/master/_examples/mvc
func (app *Application) Handle(controller interface{}) *Application {
app.handle(controller)
return app
}
// HandleWebsocket handles a websocket specific controller.
// Its exported methods are the events.
// If a "Namespace" field or method exists then namespace is set, otherwise empty namespace.
// Note that a websocket controller is registered and ran under a specific connection connected to a namespace
// and it cannot send HTTP responses on that state.
// However all static and dynamic dependency injection features are working, as expected, like any regular MVC Controller.
func (app *Application) HandleWebsocket(controller interface{}) {
c := app.handle(controller)
c.markAsWebsocket()
}
var _ websocket.ConnHandler = (*Application)(nil)
// GetNamespaces completes the websocket ConnHandler interface.
// It returns a collection of namespace and events that
// were registered through `HandleWebsocket` controllers.
func (app *Application) GetNamespaces() websocket.Namespaces {
makeInjector := func(injector *di.StructInjector) websocket.StructInjector {
return func(_ reflect.Type, nsConn *websocket.NSConn) reflect.Value {
v := injector.Acquire()
if injector.CanInject {
injector.InjectElem(v.Elem(), reflect.ValueOf(websocket.GetContext(nsConn.Conn)))
}
return v
}
}
var websocketControllers []websocket.ConnHandler
for _, c := range app.Controllers {
if c.servesWebsocket {
wsInjector := makeInjector(c.injector)
s := websocket.NewStruct(c.Value).SetInjector(wsInjector)
websocketControllers = append(websocketControllers, s)
}
}
return websocket.JoinConnHandlers(websocketControllers...).GetNamespaces()
}
func (app *Application) handle(controller interface{}) *ControllerActivator {
// initialize the controller's activator, nothing too magical so far.
c := newControllerActivator(app.Router, controller, app.Dependencies, app.ErrorHandler)
@@ -193,7 +241,7 @@ func (app *Application) Handle(controller interface{}) *Application {
}
app.Controllers = append(app.Controllers, c)
return app
return c
}
// HandleError registers a `hero.ErrorHandlerFunc` which will be fired when

View File

@@ -1,48 +0,0 @@
package mvc
import (
"github.com/kataras/iris/context"
"github.com/kataras/iris/sessions"
)
var defaultSessionManager = sessions.New(sessions.Config{})
// SessionController is a simple `Controller` implementation
// which requires a binded session manager in order to give
// direct access to the current client's session via its `Session` field.
//
// SessionController is deprecated please use the new dependency injection's methods instead,
// i.e `mvcApp.Register(sessions.New(sessions.Config{}).Start)`.
// It's more controlled by you,
// also *sessions.Session type can now `Destroy` itself without the need of the manager, embrace it.
type SessionController struct {
Manager *sessions.Sessions
Session *sessions.Session
}
// BeforeActivation called, once per application lifecycle NOT request,
// every single time the dev registers a specific SessionController-based controller.
// It makes sure that its "Manager" field is filled
// even if the caller didn't provide any sessions manager via the MVC's Application's `Handle` function.
func (s *SessionController) BeforeActivation(b BeforeActivation) {
if didntBindManually := b.Dependencies().AddOnce(defaultSessionManager); didntBindManually {
b.Router().GetReporter().Add(
`MVC SessionController: couldn't find any "*sessions.Sessions" bindable value to fill the "Manager" field,
therefore this controller is using the default sessions manager instead.
Please refer to the documentation to learn how you can provide the session manager`)
}
}
// BeginRequest initializes the current user's Session.
func (s *SessionController) BeginRequest(ctx context.Context) {
if s.Manager == nil {
ctx.Application().Logger().Errorf(`MVC SessionController: sessions manager is nil, report this as a bug
because the SessionController should predict this on its activation state and use a default one automatically`)
return
}
s.Session = s.Manager.Start(ctx)
}
// EndRequest is here to complete the `BaseController`.
func (s *SessionController) EndRequest(ctx context.Context) {}