diff --git a/_examples/mvc/login/main.go b/_examples/mvc/login/main.go index fffe66c6..105f9610 100644 --- a/_examples/mvc/login/main.go +++ b/_examples/mvc/login/main.go @@ -5,12 +5,14 @@ package main import ( "time" - "github.com/kataras/iris" "github.com/kataras/iris/_examples/mvc/login/datasource" "github.com/kataras/iris/_examples/mvc/login/repositories" "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/middleware" + + "github.com/kataras/iris" + "github.com/kataras/iris/mvc" "github.com/kataras/iris/sessions" ) @@ -34,7 +36,9 @@ func main() { ctx.View("shared/error.html") }) - // Create our repositories and services. + // ---- Register our controllers. ---- + + // Prepare our repositories and services. db, err := datasource.LoadUsers(datasource.Memory) if err != nil { app.Logger().Fatalf("error while loading the users: %v", err) @@ -43,29 +47,38 @@ func main() { repo := repositories.NewUserRepository(db) userService := services.NewUserService(repo) - // Register our controllers. - app.Controller("/users", new(controllers.UsersController), - // Add the basic authentication(admin:password) middleware - // for the /users based requests. - middleware.BasicAuth, - // Bind the "userService" to the UserController's Service (interface) field. - userService, - ) + // "/users" based mvc application. + users := mvc.New(app.Party("/users")) + // Add the basic authentication(admin:password) middleware + // for the /users based requests. + users.Router.Use(middleware.BasicAuth) + // Bind the "userService" to the UserController's Service (interface) field. + users.AddDependencies(userService) + users.Register(new(controllers.UsersController)) + // "/user" based mvc application. sessManager := sessions.New(sessions.Config{ Cookie: "sessioncookiename", 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/hello - // http://localhost:8080/hello/iris + // http://localhost:8080/noexist + // and all controller's methods like // http://localhost:8080/users/1 app.Run( + // Starts the web server at localhost:8080 iris.Addr("localhost:8080"), + // Disables the updater. iris.WithoutVersionChecker, + // Ignores err server closed log when CTRL/CMD+C pressed. iris.WithoutServerError(iris.ErrServerClosed), - iris.WithOptimizations, // enables faster json serialization and more + // Enables faster json serialization and more. + iris.WithOptimizations, ) } diff --git a/_examples/mvc/login/web/controllers/user_controller.go b/_examples/mvc/login/web/controllers/user_controller.go index e4bda39d..f246595b 100644 --- a/_examples/mvc/login/web/controllers/user_controller.go +++ b/_examples/mvc/login/web/controllers/user_controller.go @@ -6,7 +6,7 @@ import ( "github.com/kataras/iris/_examples/mvc/login/datamodels" "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/sessions" ) @@ -20,39 +20,21 @@ import ( // GET /user/me // All HTTP Methods /user/logout type UserController struct { - // mvc.C is just a lightweight lightweight alternative - // to the "mvc.Controller" controller type, - // use it when you don't need mvc.Controller's fields - // (you don't need those fields when you return values from the method functions). - 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) + // and the Session which depends on the current context (dynamic binding). + Ctx iris.Context // Our UserService, it's an interface which // is binded from the main application. Service services.UserService - // Session-relative things. - Manager *sessions.Sessions + // Session, binded using dependency injection from the main.go. 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" func (c *UserController) getCurrentUserID() int64 { @@ -65,12 +47,12 @@ func (c *UserController) isLoggedIn() bool { } func (c *UserController) logout() { - c.Manager.DestroyByID(c.Session.ID()) + c.Session.Destroy() } var registerStaticView = mvc.View{ Name: "user/register.html", - Data: context.Map{"Title": "User Registration"}, + Data: iris.Map{"Title": "User Registration"}, } // GetRegister handles GET: http://localhost:8080/user/register. @@ -119,7 +101,7 @@ func (c *UserController) PostRegister() mvc.Result { var loginStaticView = mvc.View{ Name: "user/login.html", - Data: context.Map{"Title": "User Login"}, + Data: iris.Map{"Title": "User Login"}, } // GetLogin handles GET: http://localhost:8080/user/login. @@ -172,7 +154,7 @@ func (c *UserController) GetMe() mvc.Result { return mvc.View{ Name: "user/me.html", - Data: context.Map{ + Data: iris.Map{ "Title": "Profile of " + u.Username, "User": u, }, diff --git a/_examples/mvc/login/web/controllers/users_controller.go b/_examples/mvc/login/web/controllers/users_controller.go index f3f7b80f..38d42a1a 100644 --- a/_examples/mvc/login/web/controllers/users_controller.go +++ b/_examples/mvc/login/web/controllers/users_controller.go @@ -4,7 +4,7 @@ import ( "github.com/kataras/iris/_examples/mvc/login/datamodels" "github.com/kataras/iris/_examples/mvc/login/services" - "github.com/kataras/iris/mvc" + "github.com/kataras/iris" ) // UsersController is our /users API controller. @@ -14,8 +14,14 @@ import ( // DELETE /users/{id:long} | delete by id // Requires basic authentication. 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 } diff --git a/mvc/controller.go b/mvc/controller.go index 33678313..ec2d3211 100644 --- a/mvc/controller.go +++ b/mvc/controller.go @@ -2,6 +2,7 @@ package mvc import ( "fmt" + "github.com/kataras/golog" "reflect" "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 -> if c.injector == nil { 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. route := c.Router.Handle(method, path, append(middleware, handler)...) @@ -235,10 +244,10 @@ func (c *ControllerActivator) Handle(method, path, funcName string, middleware . 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 // 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 ( hasStructInjector = structInjector != nil && structInjector.Valid hasFuncInjector = funcInjector != nil && funcInjector.Valid diff --git a/mvc/di/func.go b/mvc/di/func.go index be206432..e80d59cf 100644 --- a/mvc/di/func.go +++ b/mvc/di/func.go @@ -1,6 +1,9 @@ package di -import "reflect" +import ( + "fmt" + "reflect" +) type ( targetFuncInput struct { @@ -18,7 +21,9 @@ type ( Length int // Valid is True when `Length` is > 0, it's statically set-ed for // 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 = len(s.inputs) 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 } +func (s *FuncInjector) String() string { + return s.trace +} + func (s *FuncInjector) Inject(in *[]reflect.Value, ctx ...reflect.Value) { args := *in for _, input := range s.inputs { diff --git a/mvc/di/struct.go b/mvc/di/struct.go index 76e177b0..68855623 100644 --- a/mvc/di/struct.go +++ b/mvc/di/struct.go @@ -1,6 +1,9 @@ package di -import "reflect" +import ( + "fmt" + "reflect" +) type ( targetStructField struct { @@ -13,6 +16,8 @@ type ( // fields []*targetStructField 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 + + 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 } +func (s *StructInjector) String() string { + return s.trace +} + func (s *StructInjector) Inject(dest interface{}, ctx ...reflect.Value) { if dest == nil { return diff --git a/sessions/session.go b/sessions/session.go index 266f7881..bb9b46f2 100644 --- a/sessions/session.go +++ b/sessions/session.go @@ -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. func (s *Session) ID() string { return s.sid