mirror of
https://github.com/kataras/iris.git
synced 2025-12-22 20:37:05 +00:00
Update sessions/sessiondb/badger to its latest version and SessionController is able to initialize itself if session manager is not provided by the caller-dev.
Changes:
Update the badger database using transactions, as supported from yesterday via: 06242925c2
Add a custom `OnActivate` via mvc/activator/activate_listener.go, this can be used to perform any custom actions when the app registers the supported Controllers. See mvc/session_controller.go for the excellent use case.
errors.Reporter.AddErr returns true if the error is added to the stack, otherwise false
Former-commit-id: c896a2d186a4315643f3c5fdb4325f7ee48a9e0a
This commit is contained in:
78
mvc/activator/activate_listener.go
Normal file
78
mvc/activator/activate_listener.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package activator
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// CallOnActivate simply calls the "controller"'s `OnActivate(*ActivatePayload)` function,
|
||||
// if any.
|
||||
//
|
||||
// Look `activator.go#Register` and `ActivateListener` for more.
|
||||
func CallOnActivate(controller interface{},
|
||||
bindValues *[]interface{}, registerFunc RegisterFunc) {
|
||||
|
||||
if ac, ok := controller.(ActivateListener); ok {
|
||||
p := &ActivatePayload{
|
||||
BindValues: bindValues,
|
||||
Handle: registerFunc,
|
||||
}
|
||||
ac.OnActivate(p)
|
||||
}
|
||||
}
|
||||
|
||||
// ActivateListener is an interface which should be declared
|
||||
// on a Controller which needs to register or change the bind values
|
||||
// that the caller-"user" has been passed to; via the `app.Controller`.
|
||||
// If that interface is completed by a controller
|
||||
// then the `OnActivate` function will be called ONCE, NOT in every request
|
||||
// but ONCE at the application's lifecycle.
|
||||
type ActivateListener interface {
|
||||
// OnActivate accepts a pointer to the `ActivatePayload`.
|
||||
//
|
||||
// The `Controller` can make use of the `OnActivate` function
|
||||
// to register custom routes
|
||||
// or modify the provided values that will be binded to the
|
||||
// controller later on.
|
||||
//
|
||||
// Look `ActivatePayload` for more.
|
||||
OnActivate(*ActivatePayload)
|
||||
}
|
||||
|
||||
// ActivatePayload contains the necessary information and the ability
|
||||
// to alt a controller's registration options, i.e the binder.
|
||||
//
|
||||
// With `ActivatePayload` the `Controller` can register custom routes
|
||||
// or modify the provided values that will be binded to the
|
||||
// controller later on.
|
||||
type ActivatePayload struct {
|
||||
BindValues *[]interface{}
|
||||
Handle RegisterFunc
|
||||
}
|
||||
|
||||
// EnsureBindValue will make sure that this "bindValue"
|
||||
// will be registered to the controller's binder
|
||||
// if its type is not already passed by the caller..
|
||||
//
|
||||
// For example, on `SessionController` it looks if *sessions.Sessions
|
||||
// has been binded from the caller and if not then the "bindValue"
|
||||
// will be binded and used as a default sessions manager instead.
|
||||
//
|
||||
// At general, if the caller has already provided a value with the same Type
|
||||
// then the "bindValue" will be ignored and not be added to the controller's bind values.
|
||||
//
|
||||
// Returns true if the caller has NOT already provided a value with the same Type
|
||||
// and "bindValue" is NOT ignored therefore is appended to the controller's bind values.
|
||||
func (i *ActivatePayload) EnsureBindValue(bindValue interface{}) bool {
|
||||
valueTyp := reflect.TypeOf(bindValue)
|
||||
localBindValues := *i.BindValues
|
||||
|
||||
for _, bindedValue := range localBindValues {
|
||||
// type already exists, remember: binding here is per-type.
|
||||
if reflect.TypeOf(bindedValue) == valueTyp {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
*i.BindValues = append(localBindValues, bindValue)
|
||||
return true
|
||||
}
|
||||
@@ -65,7 +65,6 @@ type BaseController interface {
|
||||
|
||||
// ActivateController returns a new controller type info description.
|
||||
func ActivateController(base BaseController, bindValues []interface{}) (TController, error) {
|
||||
|
||||
// get and save the type.
|
||||
typ := reflect.TypeOf(base)
|
||||
if typ.Kind() != reflect.Ptr {
|
||||
@@ -202,6 +201,8 @@ func RegisterMethodHandlers(t TController, registerFunc RegisterFunc) {
|
||||
func Register(controller BaseController, bindValues []interface{},
|
||||
registerFunc RegisterFunc) error {
|
||||
|
||||
CallOnActivate(controller, &bindValues, registerFunc)
|
||||
|
||||
t, err := ActivateController(controller, bindValues)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/mvc"
|
||||
"github.com/kataras/iris/mvc/activator"
|
||||
|
||||
"github.com/kataras/iris/core/router"
|
||||
"github.com/kataras/iris/httptest"
|
||||
@@ -493,3 +494,33 @@ func TestControllerRelPathFromFunc(t *testing.T) {
|
||||
e.GET("/anything/here").Expect().Status(httptest.StatusOK).
|
||||
Body().Equal("GET:/anything/here")
|
||||
}
|
||||
|
||||
type testControllerActivateListener struct {
|
||||
mvc.Controller
|
||||
|
||||
TitlePointer *testBindType
|
||||
}
|
||||
|
||||
func (c *testControllerActivateListener) OnActivate(p *activator.ActivatePayload) {
|
||||
p.EnsureBindValue(&testBindType{
|
||||
title: "default title",
|
||||
})
|
||||
}
|
||||
|
||||
func (c *testControllerActivateListener) Get() {
|
||||
c.Text = c.TitlePointer.title
|
||||
}
|
||||
|
||||
func TestControllerActivateListener(t *testing.T) {
|
||||
app := iris.New()
|
||||
app.Controller("/", new(testControllerActivateListener))
|
||||
app.Controller("/manual", new(testControllerActivateListener), &testBindType{
|
||||
title: "my title",
|
||||
})
|
||||
|
||||
e := httptest.New(t, app)
|
||||
e.GET("/").Expect().Status(httptest.StatusOK).
|
||||
Body().Equal("default title")
|
||||
e.GET("/manual").Expect().Status(httptest.StatusOK).
|
||||
Body().Equal("my title")
|
||||
}
|
||||
|
||||
19
mvc/go19.go
Normal file
19
mvc/go19.go
Normal file
@@ -0,0 +1,19 @@
|
||||
// +build go1.9
|
||||
|
||||
package mvc
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/mvc/activator"
|
||||
)
|
||||
|
||||
// ActivatePayload contains the necessary information and the ability
|
||||
// to alt a controller's registration options, i.e the binder.
|
||||
//
|
||||
// With `ActivatePayload` the `Controller` can register custom routes
|
||||
// or modify the provided values that will be binded to the
|
||||
// controller later on.
|
||||
//
|
||||
// Look the `mvc/activator#ActivatePayload` for its implementation.
|
||||
//
|
||||
// A shortcut for the `mvc/activator#ActivatePayload`, useful when `OnActivate` is being used.
|
||||
type ActivatePayload = activator.ActivatePayload
|
||||
@@ -2,9 +2,14 @@ package mvc
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/mvc/activator"
|
||||
"github.com/kataras/iris/sessions"
|
||||
|
||||
"github.com/kataras/golog"
|
||||
)
|
||||
|
||||
var defaultManager = 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.
|
||||
@@ -15,21 +20,27 @@ type SessionController struct {
|
||||
Session *sessions.Session
|
||||
}
|
||||
|
||||
var managerMissing = "MVC SessionController: session manager field is nil, you have to bind it to a *sessions.Sessions"
|
||||
// OnActivate 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 `app.Controller` function.
|
||||
func (s *SessionController) OnActivate(p *activator.ActivatePayload) {
|
||||
if p.EnsureBindValue(defaultManager) {
|
||||
golog.Warnf(`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 calls the Controller's BeginRequest
|
||||
// and tries to initialize the current user's Session.
|
||||
func (s *SessionController) BeginRequest(ctx context.Context) {
|
||||
s.Controller.BeginRequest(ctx)
|
||||
if s.Manager == nil {
|
||||
ctx.Application().Logger().Errorf(managerMissing)
|
||||
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)
|
||||
}
|
||||
|
||||
/* TODO:
|
||||
Maybe add struct tags on `binder` for required binded values
|
||||
in order to log error if some of the bindings are missing or leave that to the end-developers?
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user