1
0
mirror of https://github.com/kataras/iris.git synced 2025-12-20 03:17:04 +00:00

Update the _examples/mvc/login example - add a Path property at mvc.Response and add a context.Proceed helper function

Former-commit-id: b898901fe4a324e888a6e09c93530cf7a551cf2a
This commit is contained in:
Gerasimos (Makis) Maropoulos
2017-10-12 16:28:41 +03:00
parent b0f8329768
commit 32d14db46d
33 changed files with 1073 additions and 50 deletions

View File

@@ -0,0 +1,189 @@
// file: controllers/user_controller.go
package controllers
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/mvc"
"github.com/kataras/iris/sessions"
)
// UserController is our /user controller.
// UserController is responsible to handle the following requests:
// GET /user/register
// POST /user/register
// GET /user/login
// POST /user/login
// 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
// Our UserService, it's an interface which
// is binded from the main application.
Service services.UserService
// Session-relative things.
Manager *sessions.Sessions
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 {
userID, _ := c.Session.GetInt64Default(userIDKey, 0)
return userID
}
func (c *UserController) isLoggedIn() bool {
return c.getCurrentUserID() > 0
}
func (c *UserController) logout() {
c.Manager.DestroyByID(c.Session.ID())
}
var registerStaticView = mvc.View{
Name: "user/register.html",
Data: context.Map{"Title": "User Registration"},
}
// GetRegister handles GET: http://localhost:8080/user/register.
func (c *UserController) GetRegister() mvc.Result {
if c.isLoggedIn() {
c.logout()
}
return registerStaticView
}
// PostRegister handles POST: http://localhost:8080/user/register.
func (c *UserController) PostRegister() mvc.Result {
// get firstname, username and password from the form.
var (
firstname = c.Ctx.FormValue("firstname")
username = c.Ctx.FormValue("username")
password = c.Ctx.FormValue("password")
)
// create the new user, the password will be hashed by the service.
u, err := c.Service.Create(password, datamodels.User{
Username: username,
Firstname: firstname,
})
// set the user's id to this session even if err != nil,
// the zero id doesn't matters because .getCurrentUserID() checks for that.
// If err != nil then it will be shown, see below on mvc.Response.Err: err.
c.Session.Set(userIDKey, u.ID)
return mvc.Response{
// if not nil then this error will be shown instead.
Err: err,
// redirect to /user/me.
Path: "/user/me",
// When redirecting from POST to GET request you -should- use this HTTP status code,
// however there're some (complicated) alternatives if you
// search online or even the HTTP RFC.
// Status "See Other" RFC 7231, however iris can automatically fix that
// but it's good to know you can set a custom code;
// Code: 303,
}
}
var loginStaticView = mvc.View{
Name: "user/login.html",
Data: context.Map{"Title": "User Login"},
}
// GetLogin handles GET: http://localhost:8080/user/login.
func (c *UserController) GetLogin() mvc.Result {
if c.isLoggedIn() {
// if it's already logged in then destroy the previous session.
c.logout()
}
return loginStaticView
}
// PostLogin handles POST: http://localhost:8080/user/register.
func (c *UserController) PostLogin() mvc.Result {
var (
username = c.Ctx.FormValue("username")
password = c.Ctx.FormValue("password")
)
u, found := c.Service.GetByUsernameAndPassword(username, password)
if !found {
return mvc.Response{
Path: "/user/register",
}
}
c.Session.Set(userIDKey, u.ID)
return mvc.Response{
Path: "/user/me",
}
}
// GetMe handles GET: http://localhost:8080/user/me.
func (c *UserController) GetMe() mvc.Result {
if !c.isLoggedIn() {
// if it's not logged in then redirect user to the login page.
return mvc.Response{Path: "/user/login"}
}
u, found := c.Service.GetByID(c.getCurrentUserID())
if !found {
// if the session exists but for some reason the user doesn't exist in the "database"
// then logout and re-execute the function, it will redirect the client to the
// /user/login page.
c.logout()
return c.GetMe()
}
return mvc.View{
Name: "user/me.html",
Data: context.Map{
"Title": "Profile of " + u.Username,
"User": u,
},
}
}
// AnyLogout handles All/Any HTTP Methods for: http://localhost:8080/user/logout.
func (c *UserController) AnyLogout() {
if c.isLoggedIn() {
c.logout()
}
c.Ctx.Redirect("/user/login")
}

View File

@@ -0,0 +1,101 @@
package controllers
import (
"github.com/kataras/iris/_examples/mvc/login/datamodels"
"github.com/kataras/iris/_examples/mvc/login/services"
"github.com/kataras/iris/mvc"
)
// UsersController is our /users API controller.
// GET /users | get all
// GET /users/{id:long} | get by id
// PUT /users/{id:long} | update by id
// DELETE /users/{id:long} | delete by id
// Requires basic authentication.
type UsersController struct {
mvc.C
Service services.UserService
}
// This could be possible but we should not call handlers inside the `BeginRequest`.
// Because `BeginRequest` was introduced to set common, shared variables between all method handlers
// before their execution.
// We will add this middleware from our `app.Controller` call.
//
// var authMiddleware = basicauth.New(basicauth.Config{
// Users: map[string]string{
// "admin": "password",
// },
// })
//
// func (c *UsersController) BeginRequest(ctx iris.Context) {
// c.C.BeginRequest(ctx)
//
// if !ctx.Proceed(authMiddleware) {
// ctx.StopExecution()
// }
// }
// Get returns list of the users.
// Demo:
// curl -i -u admin:password http://localhost:8080/users
//
// The correct way if you have sensitive data:
// func (c *UsersController) Get() (results []viewmodels.User) {
// data := c.Service.GetAll()
//
// for _, user := range data {
// results = append(results, viewmodels.User{user})
// }
// return
// }
// otherwise just return the datamodels.
func (c *UsersController) Get() (results []datamodels.User) {
return c.Service.GetAll()
}
// GetBy returns a user.
// Demo:
// curl -i -u admin:password http://localhost:8080/users/1
func (c *UsersController) GetBy(id int64) (user datamodels.User, found bool) {
u, found := c.Service.GetByID(id)
if !found {
// this message will be binded to the
// main.go -> app.OnAnyErrorCode -> NotFound -> shared/error.html -> .Message text.
c.Ctx.Values().Set("message", "User couldn't be found!")
}
return u, found // it will throw/emit 404 if found == false.
}
// PutBy updates a user.
// Demo:
// curl -i -X PUT -u admin:password -F "username=kataras"
// -F "password=rawPasswordIsNotSafeIfOrNotHTTPs_You_Should_Use_A_client_side_lib_for_hash_as_well"
// http://localhost:8080/users/1
func (c *UsersController) PutBy(id int64) (datamodels.User, error) {
// username := c.Ctx.FormValue("username")
// password := c.Ctx.FormValue("password")
u := datamodels.User{}
if err := c.Ctx.ReadForm(&u); err != nil {
return u, err
}
return c.Service.Update(id, u)
}
// DeleteBy deletes a user.
// Demo:
// curl -i -X DELETE -u admin:password http://localhost:8080/users/1
func (c *UsersController) DeleteBy(id int64) interface{} {
wasDel := c.Service.DeleteByID(id)
if wasDel {
// return the deleted user's ID
return map[string]interface{}{"deleted": id}
}
// right here we can see that a method function
// can return any of those two types(map or int),
// we don't have to specify the return type to a specific type.
return 400 // same as `iris.StatusBadRequest`.
}