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:
@@ -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)
|
||||
|
||||
|
||||
50
mvc/mvc.go
50
mvc/mvc.go
@@ -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
|
||||
|
||||
@@ -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) {}
|
||||
Reference in New Issue
Block a user