Add session.Destroy (before I've added: Increment & Decrement entry helpers as well) | Improve the debug messages for the controllers

Former-commit-id: f5f17b05252a5032ace1e2d0fdd2304fc4004635
This commit is contained in:
Gerasimos (Makis) Maropoulos 2017-12-16 23:09:00 +02:00
parent b8cafce6b9
commit 68cc6641d4
7 changed files with 119 additions and 53 deletions

View File

@ -5,12 +5,14 @@ package main
import ( import (
"time" "time"
"github.com/kataras/iris"
"github.com/kataras/iris/_examples/mvc/login/datasource" "github.com/kataras/iris/_examples/mvc/login/datasource"
"github.com/kataras/iris/_examples/mvc/login/repositories" "github.com/kataras/iris/_examples/mvc/login/repositories"
"github.com/kataras/iris/_examples/mvc/login/services" "github.com/kataras/iris/_examples/mvc/login/services"
"github.com/kataras/iris/_examples/mvc/login/web/controllers" "github.com/kataras/iris/_examples/mvc/login/web/controllers"
"github.com/kataras/iris/_examples/mvc/login/web/middleware" "github.com/kataras/iris/_examples/mvc/login/web/middleware"
"github.com/kataras/iris"
"github.com/kataras/iris/mvc"
"github.com/kataras/iris/sessions" "github.com/kataras/iris/sessions"
) )
@ -34,7 +36,9 @@ func main() {
ctx.View("shared/error.html") ctx.View("shared/error.html")
}) })
// Create our repositories and services. // ---- Register our controllers. ----
// Prepare our repositories and services.
db, err := datasource.LoadUsers(datasource.Memory) db, err := datasource.LoadUsers(datasource.Memory)
if err != nil { if err != nil {
app.Logger().Fatalf("error while loading the users: %v", err) app.Logger().Fatalf("error while loading the users: %v", err)
@ -43,29 +47,38 @@ func main() {
repo := repositories.NewUserRepository(db) repo := repositories.NewUserRepository(db)
userService := services.NewUserService(repo) userService := services.NewUserService(repo)
// Register our controllers. // "/users" based mvc application.
app.Controller("/users", new(controllers.UsersController), users := mvc.New(app.Party("/users"))
// Add the basic authentication(admin:password) middleware // Add the basic authentication(admin:password) middleware
// for the /users based requests. // for the /users based requests.
middleware.BasicAuth, users.Router.Use(middleware.BasicAuth)
// Bind the "userService" to the UserController's Service (interface) field. // Bind the "userService" to the UserController's Service (interface) field.
userService, users.AddDependencies(userService)
) users.Register(new(controllers.UsersController))
// "/user" based mvc application.
sessManager := sessions.New(sessions.Config{ sessManager := sessions.New(sessions.Config{
Cookie: "sessioncookiename", Cookie: "sessioncookiename",
Expires: 24 * time.Hour, Expires: 24 * time.Hour,
}) })
app.Controller("/user", new(controllers.UserController), userService, sessManager) user := mvc.New(app.Party("/user"))
user.AddDependencies(
userService,
mvc.Session(sessManager),
)
user.Register(new(controllers.UserController))
// Start the web server at localhost:8080 // http://localhost:8080/noexist
// http://localhost:8080/hello // and all controller's methods like
// http://localhost:8080/hello/iris
// http://localhost:8080/users/1 // http://localhost:8080/users/1
app.Run( app.Run(
// Starts the web server at localhost:8080
iris.Addr("localhost:8080"), iris.Addr("localhost:8080"),
// Disables the updater.
iris.WithoutVersionChecker, iris.WithoutVersionChecker,
// Ignores err server closed log when CTRL/CMD+C pressed.
iris.WithoutServerError(iris.ErrServerClosed), iris.WithoutServerError(iris.ErrServerClosed),
iris.WithOptimizations, // enables faster json serialization and more // Enables faster json serialization and more.
iris.WithOptimizations,
) )
} }

View File

@ -6,7 +6,7 @@ import (
"github.com/kataras/iris/_examples/mvc/login/datamodels" "github.com/kataras/iris/_examples/mvc/login/datamodels"
"github.com/kataras/iris/_examples/mvc/login/services" "github.com/kataras/iris/_examples/mvc/login/services"
"github.com/kataras/iris/context" "github.com/kataras/iris"
"github.com/kataras/iris/mvc" "github.com/kataras/iris/mvc"
"github.com/kataras/iris/sessions" "github.com/kataras/iris/sessions"
) )
@ -20,39 +20,21 @@ import (
// GET /user/me // GET /user/me
// All HTTP Methods /user/logout // All HTTP Methods /user/logout
type UserController struct { type UserController struct {
// mvc.C is just a lightweight lightweight alternative // context is auto-binded by Iris on each request,
// to the "mvc.Controller" controller type, // remember that on each incoming request iris creates a new UserController each time,
// use it when you don't need mvc.Controller's fields // so all fields are request-scoped by-default, only dependency injection is able to set
// (you don't need those fields when you return values from the method functions). // custom fields like the Service which is the same for all requests (static binding)
mvc.C // and the Session which depends on the current context (dynamic binding).
Ctx iris.Context
// Our UserService, it's an interface which // Our UserService, it's an interface which
// is binded from the main application. // is binded from the main application.
Service services.UserService Service services.UserService
// Session-relative things. // Session, binded using dependency injection from the main.go.
Manager *sessions.Sessions
Session *sessions.Session Session *sessions.Session
} }
// BeginRequest will set the current session to the controller.
//
// Remember: iris.Context and context.Context is exactly the same thing,
// iris.Context is just a type alias for go 1.9 users.
// We use context.Context here because we don't need all iris' root functions,
// when we see the import paths, we make it visible to ourselves that this file is using only the context.
func (c *UserController) BeginRequest(ctx context.Context) {
c.C.BeginRequest(ctx)
if c.Manager == nil {
ctx.Application().Logger().Errorf(`UserController: sessions manager is nil, you should bind it`)
ctx.StopExecution() // dont run the main method handler and any "done" handlers.
return
}
c.Session = c.Manager.Start(ctx)
}
const userIDKey = "UserID" const userIDKey = "UserID"
func (c *UserController) getCurrentUserID() int64 { func (c *UserController) getCurrentUserID() int64 {
@ -65,12 +47,12 @@ func (c *UserController) isLoggedIn() bool {
} }
func (c *UserController) logout() { func (c *UserController) logout() {
c.Manager.DestroyByID(c.Session.ID()) c.Session.Destroy()
} }
var registerStaticView = mvc.View{ var registerStaticView = mvc.View{
Name: "user/register.html", Name: "user/register.html",
Data: context.Map{"Title": "User Registration"}, Data: iris.Map{"Title": "User Registration"},
} }
// GetRegister handles GET: http://localhost:8080/user/register. // GetRegister handles GET: http://localhost:8080/user/register.
@ -119,7 +101,7 @@ func (c *UserController) PostRegister() mvc.Result {
var loginStaticView = mvc.View{ var loginStaticView = mvc.View{
Name: "user/login.html", Name: "user/login.html",
Data: context.Map{"Title": "User Login"}, Data: iris.Map{"Title": "User Login"},
} }
// GetLogin handles GET: http://localhost:8080/user/login. // GetLogin handles GET: http://localhost:8080/user/login.
@ -172,7 +154,7 @@ func (c *UserController) GetMe() mvc.Result {
return mvc.View{ return mvc.View{
Name: "user/me.html", Name: "user/me.html",
Data: context.Map{ Data: iris.Map{
"Title": "Profile of " + u.Username, "Title": "Profile of " + u.Username,
"User": u, "User": u,
}, },

View File

@ -4,7 +4,7 @@ import (
"github.com/kataras/iris/_examples/mvc/login/datamodels" "github.com/kataras/iris/_examples/mvc/login/datamodels"
"github.com/kataras/iris/_examples/mvc/login/services" "github.com/kataras/iris/_examples/mvc/login/services"
"github.com/kataras/iris/mvc" "github.com/kataras/iris"
) )
// UsersController is our /users API controller. // UsersController is our /users API controller.
@ -14,8 +14,14 @@ import (
// DELETE /users/{id:long} | delete by id // DELETE /users/{id:long} | delete by id
// Requires basic authentication. // Requires basic authentication.
type UsersController struct { type UsersController struct {
mvc.C // context is auto-binded by Iris on each request,
// remember that on each incoming request iris creates a new UserController each time,
// so all fields are request-scoped by-default, only dependency injection is able to set
// custom fields like the Service which is the same for all requests (static binding).
Ctx iris.Context
// Our UserService, it's an interface which
// is binded from the main application.
Service services.UserService Service services.UserService
} }

View File

@ -2,6 +2,7 @@ package mvc
import ( import (
"fmt" "fmt"
"github.com/kataras/golog"
"reflect" "reflect"
"strings" "strings"
@ -220,9 +221,17 @@ func (c *ControllerActivator) Handle(method, path, funcName string, middleware .
// this is a logical flow, so we will choose that one -> // this is a logical flow, so we will choose that one ->
if c.injector == nil { if c.injector == nil {
c.injector = c.Dependencies.Struct(c.Value) c.injector = c.Dependencies.Struct(c.Value)
if c.injector.Valid {
golog.Debugf("MVC dependencies of '%s':\n%s", c.FullName, c.injector.String())
}
} }
handler := buildHandler(m, c.Type, c.Value, c.injector, funcInjector, funcIn) if funcInjector.Valid {
golog.Debugf("MVC dependencies of method '%s.%s':\n%s", c.FullName, funcName, funcInjector.String())
}
handler := buildControllerHandler(m, c.Type, c.Value, c.injector, funcInjector, funcIn)
// register the handler now. // register the handler now.
route := c.Router.Handle(method, path, append(middleware, handler)...) route := c.Router.Handle(method, path, append(middleware, handler)...)
@ -235,10 +244,10 @@ func (c *ControllerActivator) Handle(method, path, funcName string, middleware .
return route return route
} }
// buildHandler has many many dublications but we do that to achieve the best // buildControllerHandler has many many dublications but we do that to achieve the best
// performance possible, to use the information we know // performance possible, to use the information we know
// and calculate what is needed and what not in serve-time. // and calculate what is needed and what not in serve-time.
func buildHandler(m reflect.Method, typ reflect.Type, initRef reflect.Value, structInjector *di.StructInjector, funcInjector *di.FuncInjector, funcIn []reflect.Type) context.Handler { func buildControllerHandler(m reflect.Method, typ reflect.Type, initRef reflect.Value, structInjector *di.StructInjector, funcInjector *di.FuncInjector, funcIn []reflect.Type) context.Handler {
var ( var (
hasStructInjector = structInjector != nil && structInjector.Valid hasStructInjector = structInjector != nil && structInjector.Valid
hasFuncInjector = funcInjector != nil && funcInjector.Valid hasFuncInjector = funcInjector != nil && funcInjector.Valid

View File

@ -1,6 +1,9 @@
package di package di
import "reflect" import (
"fmt"
"reflect"
)
type ( type (
targetFuncInput struct { targetFuncInput struct {
@ -18,7 +21,9 @@ type (
Length int Length int
// Valid is True when `Length` is > 0, it's statically set-ed for // Valid is True when `Length` is > 0, it's statically set-ed for
// performance reasons. // performance reasons.
Valid bool // Valid bool
trace string // for debug info.
} }
) )
@ -83,9 +88,29 @@ func MakeFuncInjector(fn reflect.Value, hijack Hijacker, goodFunc TypeChecker, v
// s.Length = n // s.Length = n
s.Length = len(s.inputs) s.Length = len(s.inputs)
s.Valid = s.Length > 0 s.Valid = s.Length > 0
for i, in := range s.inputs {
bindmethodTyp := "Static"
if in.Object.BindType == Dynamic {
bindmethodTyp = "Dynamic"
}
typIn := typ.In(in.InputIndex)
// remember: on methods that are part of a struct (i.e controller)
// the input index = 1 is the begggining instead of the 0,
// because the 0 is the controller receiver pointer of the method.
s.trace += fmt.Sprintf("[%d] %s binding: '%s' for input position: %d and type: '%s'\n", i+1, bindmethodTyp, in.Object.Type.String(), in.InputIndex, typIn.String())
}
return s return s
} }
func (s *FuncInjector) String() string {
return s.trace
}
func (s *FuncInjector) Inject(in *[]reflect.Value, ctx ...reflect.Value) { func (s *FuncInjector) Inject(in *[]reflect.Value, ctx ...reflect.Value) {
args := *in args := *in
for _, input := range s.inputs { for _, input := range s.inputs {

View File

@ -1,6 +1,9 @@
package di package di
import "reflect" import (
"fmt"
"reflect"
)
type ( type (
targetStructField struct { targetStructField struct {
@ -13,6 +16,8 @@ type (
// //
fields []*targetStructField fields []*targetStructField
Valid bool // is True when contains fields and it's a valid target struct. Valid bool // is True when contains fields and it's a valid target struct.
trace string // for debug info.
} }
) )
@ -56,9 +61,25 @@ func MakeStructInjector(v reflect.Value, hijack Hijacker, goodFunc TypeChecker,
} }
s.Valid = len(s.fields) > 0 s.Valid = len(s.fields) > 0
if s.Valid {
for i, f := range s.fields {
bindmethodTyp := "Static"
if f.Object.BindType == Dynamic {
bindmethodTyp = "Dynamic"
}
elemField := s.elemType.FieldByIndex(f.FieldIndex)
s.trace += fmt.Sprintf("[%d] %s binding: '%s' for field '%s %s'\n", i+1, bindmethodTyp, f.Object.Type.String(), elemField.Name, elemField.Type.String())
}
}
return s return s
} }
func (s *StructInjector) String() string {
return s.trace
}
func (s *StructInjector) Inject(dest interface{}, ctx ...reflect.Value) { func (s *StructInjector) Inject(dest interface{}, ctx ...reflect.Value) {
if dest == nil { if dest == nil {
return return

View File

@ -31,6 +31,16 @@ type (
} }
) )
// Destroy destroys this session, it removes all sessions and flash values,
// the session entry from the server and updates the registered session databases,
// note that this method does NOT removes the client's cookie, although
// it should be re-seted if new session is attached to that (client).
//
// Use the session's manager `Destroy(ctx)` in order to remove the cookie as well.
func (s *Session) Destroy() {
s.provider.deleteSession(s)
}
// ID returns the session's ID. // ID returns the session's ID.
func (s *Session) ID() string { func (s *Session) ID() string {
return s.sid return s.sid