mirror of
https://github.com/kataras/iris.git
synced 2025-12-20 03:17:04 +00:00
Structuring examples - Pushed to iris-contrib/examples as well.
Former-commit-id: 24ee6ce233d83f0b394afc6c69b5a88243406045
This commit is contained in:
BIN
_examples/structuring/bootstrap/folder_structure.png
Normal file
BIN
_examples/structuring/bootstrap/folder_structure.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
20
_examples/structuring/bootstrap/main.go
Normal file
20
_examples/structuring/bootstrap/main.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/_examples/structuring/bootstrap/bootstrap"
|
||||
"github.com/kataras/iris/_examples/structuring/bootstrap/middleware/identity"
|
||||
"github.com/kataras/iris/_examples/structuring/bootstrap/routes"
|
||||
)
|
||||
|
||||
var app = bootstrap.New("Awesome App", "kataras2006@hotmail.com",
|
||||
identity.Configure,
|
||||
routes.Configure,
|
||||
)
|
||||
|
||||
func init() {
|
||||
app.Bootstrap()
|
||||
}
|
||||
|
||||
func main() {
|
||||
app.Listen(":8080")
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/kataras/iris/httptest"
|
||||
)
|
||||
|
||||
// TestApp runs by `$ go test -v`.
|
||||
// go test -v
|
||||
func TestApp(t *testing.T) {
|
||||
e := httptest.New(t, app.Application)
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"github.com/kataras/iris"
|
||||
|
||||
"github.com/kataras/iris/_examples/structuring/handler-based/bootstrap"
|
||||
"github.com/kataras/iris/_examples/structuring/bootstrap/bootstrap"
|
||||
)
|
||||
|
||||
// New returns a new handler which adds some headers and view data
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@@ -1,7 +1,7 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/_examples/structuring/handler-based/bootstrap"
|
||||
"github.com/kataras/iris/_examples/structuring/bootstrap/bootstrap"
|
||||
)
|
||||
|
||||
// Configure registers the necessary routes to the app.
|
||||
@@ -1,20 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/_examples/structuring/handler-based/bootstrap"
|
||||
"github.com/kataras/iris/_examples/structuring/handler-based/middleware/identity"
|
||||
"github.com/kataras/iris/_examples/structuring/handler-based/routes"
|
||||
)
|
||||
|
||||
var app = bootstrap.New("Awesome App", "kataras2006@hotmail.com",
|
||||
identity.Configure,
|
||||
routes.Configure,
|
||||
)
|
||||
|
||||
func init() {
|
||||
app.Bootstrap()
|
||||
}
|
||||
|
||||
func main() {
|
||||
app.Listen(":8080")
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
@@ -0,0 +1,43 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/_examples/structuring/login-single-responsibility-package/user"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/sessions"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
// You got full debug messages, useful when using MVC and you want to make
|
||||
// sure that your code is aligned with the Iris' MVC Architecture.
|
||||
app.Logger().SetLevel("debug")
|
||||
|
||||
app.RegisterView(iris.HTML("./views", ".html").Layout("shared/layout.html"))
|
||||
|
||||
app.StaticWeb("/public", "./public")
|
||||
|
||||
manager := sessions.New(sessions.Config{
|
||||
Cookie: "sessioncookiename",
|
||||
Expires: 24 * time.Hour,
|
||||
})
|
||||
users := user.NewDataSource()
|
||||
|
||||
app.Controller("/user", new(user.Controller), manager, users)
|
||||
|
||||
// http://localhost:8080/user/register
|
||||
// http://localhost:8080/user/login
|
||||
// http://localhost:8080/user/me
|
||||
// http://localhost:8080/user/logout
|
||||
// http://localhost:8080/user/1
|
||||
app.Run(iris.Addr(":8080"), configure)
|
||||
}
|
||||
|
||||
func configure(app *iris.Application) {
|
||||
app.Configure(
|
||||
iris.WithoutServerError(iris.ErrServerClosed),
|
||||
iris.WithCharset("UTF-8"),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/* Bordered form */
|
||||
form {
|
||||
border: 3px solid #f1f1f1;
|
||||
}
|
||||
|
||||
/* Full-width inputs */
|
||||
input[type=text], input[type=password] {
|
||||
width: 100%;
|
||||
padding: 12px 20px;
|
||||
margin: 8px 0;
|
||||
display: inline-block;
|
||||
border: 1px solid #ccc;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Set a style for all buttons */
|
||||
button {
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
padding: 14px 20px;
|
||||
margin: 8px 0;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Add a hover effect for buttons */
|
||||
button:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* Extra style for the cancel button (red) */
|
||||
.cancelbtn {
|
||||
width: auto;
|
||||
padding: 10px 18px;
|
||||
background-color: #f44336;
|
||||
}
|
||||
|
||||
/* Center the container */
|
||||
|
||||
/* Add padding to containers */
|
||||
.container {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
/* The "Forgot password" text */
|
||||
span.psw {
|
||||
float: right;
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
/* Change styles for span and cancel button on extra small screens */
|
||||
@media screen and (max-width: 300px) {
|
||||
span.psw {
|
||||
display: block;
|
||||
float: none;
|
||||
}
|
||||
.cancelbtn {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
)
|
||||
|
||||
// paths
|
||||
const (
|
||||
PathLogin = "/user/login"
|
||||
PathLogout = "/user/logout"
|
||||
)
|
||||
|
||||
// the session key for the user id comes from the Session.
|
||||
const (
|
||||
sessionIDKey = "UserID"
|
||||
)
|
||||
|
||||
// AuthController is the user authentication controller, a custom shared controller.
|
||||
type AuthController struct {
|
||||
iris.SessionController
|
||||
|
||||
Source *DataSource
|
||||
User Model `iris:"model"`
|
||||
}
|
||||
|
||||
// BeginRequest saves login state to the context, the user id.
|
||||
func (c *AuthController) BeginRequest(ctx iris.Context) {
|
||||
c.SessionController.BeginRequest(ctx)
|
||||
|
||||
if userID := c.Session.Get(sessionIDKey); userID != nil {
|
||||
ctx.Values().Set(sessionIDKey, userID)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AuthController) fireError(err error) {
|
||||
if err != nil {
|
||||
c.Ctx.Application().Logger().Debug(err.Error())
|
||||
|
||||
c.Status = 400
|
||||
c.Data["Title"] = "User Error"
|
||||
c.Data["Message"] = strings.ToUpper(err.Error())
|
||||
c.Tmpl = "shared/error.html"
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AuthController) redirectTo(id int64) {
|
||||
if id > 0 {
|
||||
c.Path = "/user/" + strconv.Itoa(int(id))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AuthController) createOrUpdate(firstname, username, password string) (user Model, err error) {
|
||||
username = strings.Trim(username, " ")
|
||||
if username == "" || password == "" || firstname == "" {
|
||||
return user, errors.New("empty firstname, username or/and password")
|
||||
}
|
||||
|
||||
userToInsert := Model{
|
||||
Firstname: firstname,
|
||||
Username: username,
|
||||
password: password,
|
||||
} // password is hashed by the Source.
|
||||
|
||||
newUser, err := c.Source.InsertOrUpdate(userToInsert)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
return newUser, nil
|
||||
}
|
||||
|
||||
func (c *AuthController) isLoggedIn() bool {
|
||||
// we don't search by session, we have the user id
|
||||
// already by the `SaveState` middleware.
|
||||
return c.Values.Get(sessionIDKey) != nil
|
||||
}
|
||||
|
||||
func (c *AuthController) verify(username, password string) (user Model, err error) {
|
||||
if username == "" || password == "" {
|
||||
return user, errors.New("please fill both username and password fields")
|
||||
}
|
||||
|
||||
u, found := c.Source.GetByUsername(username)
|
||||
if !found {
|
||||
// if user found with that username not found at all.
|
||||
return user, errors.New("user with that username does not exist")
|
||||
}
|
||||
|
||||
if ok, err := ValidatePassword(password, u.HashedPassword); err != nil || !ok {
|
||||
// if user found but an error occurred or the password is not valid.
|
||||
return user, errors.New("please try to login with valid credentials")
|
||||
}
|
||||
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// if logged in then destroy the session
|
||||
// and redirect to the login page
|
||||
// otherwise redirect to the registration page.
|
||||
func (c *AuthController) logout() {
|
||||
if c.isLoggedIn() {
|
||||
// c.Manager is the Sessions manager created
|
||||
// by the embedded SessionController, automatically.
|
||||
c.Manager.DestroyByID(c.Session.ID())
|
||||
return
|
||||
}
|
||||
|
||||
c.Path = PathLogin
|
||||
}
|
||||
|
||||
// AllowUser will check if this client is a logged user,
|
||||
// if not then it will redirect that guest to the login page
|
||||
// otherwise it will allow the execution of the next handler.
|
||||
func AllowUser(ctx iris.Context) {
|
||||
if ctx.Values().Get(sessionIDKey) != nil {
|
||||
ctx.Next()
|
||||
return
|
||||
}
|
||||
ctx.Redirect(PathLogin)
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package user
|
||||
|
||||
const (
|
||||
pathMyProfile = "/user/me"
|
||||
pathRegister = "/user/register"
|
||||
)
|
||||
|
||||
// Controller is responsible to handle the following requests:
|
||||
// GET /user/register
|
||||
// POST /user/register
|
||||
// GET /user/login
|
||||
// POST /user/login
|
||||
// GET /user/me
|
||||
// GET /user/{id:long} | long is a new param type, it's the int64.
|
||||
// All HTTP Methods /user/logout
|
||||
type Controller struct {
|
||||
AuthController
|
||||
}
|
||||
|
||||
// GetRegister handles GET:/user/register.
|
||||
func (c *Controller) GetRegister() {
|
||||
if c.isLoggedIn() {
|
||||
c.logout()
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["Title"] = "User Registration"
|
||||
c.Tmpl = pathRegister + ".html"
|
||||
}
|
||||
|
||||
// PostRegister handles POST:/user/register.
|
||||
func (c *Controller) PostRegister() {
|
||||
// we can either use the `c.Ctx.ReadForm` or read values one by one.
|
||||
var (
|
||||
firstname = c.Ctx.FormValue("firstname")
|
||||
username = c.Ctx.FormValue("username")
|
||||
password = c.Ctx.FormValue("password")
|
||||
)
|
||||
|
||||
user, err := c.createOrUpdate(firstname, username, password)
|
||||
if err != nil {
|
||||
c.fireError(err)
|
||||
return
|
||||
}
|
||||
|
||||
// setting a session value was never easier.
|
||||
c.Session.Set(sessionIDKey, user.ID)
|
||||
// succeed, nothing more to do here, just redirect to the /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.
|
||||
c.Status = 303 // "See Other" RFC 7231
|
||||
|
||||
// Redirect to GET: /user/me
|
||||
// by changing the Path (and the status code because we're in POST request at this case).
|
||||
c.Path = pathMyProfile
|
||||
}
|
||||
|
||||
// GetLogin handles GET:/user/login.
|
||||
func (c *Controller) GetLogin() {
|
||||
if c.isLoggedIn() {
|
||||
c.logout()
|
||||
return
|
||||
}
|
||||
c.Data["Title"] = "User Login"
|
||||
c.Tmpl = PathLogin + ".html"
|
||||
}
|
||||
|
||||
// PostLogin handles POST:/user/login.
|
||||
func (c *Controller) PostLogin() {
|
||||
var (
|
||||
username = c.Ctx.FormValue("username")
|
||||
password = c.Ctx.FormValue("password")
|
||||
)
|
||||
|
||||
user, err := c.verify(username, password)
|
||||
if err != nil {
|
||||
c.fireError(err)
|
||||
return
|
||||
}
|
||||
|
||||
c.Session.Set(sessionIDKey, user.ID)
|
||||
c.Path = pathMyProfile
|
||||
}
|
||||
|
||||
// AnyLogout handles any method on path /user/logout.
|
||||
func (c *Controller) AnyLogout() {
|
||||
c.logout()
|
||||
}
|
||||
|
||||
// GetMe handles GET:/user/me.
|
||||
func (c *Controller) GetMe() {
|
||||
id, err := c.Session.GetInt64(sessionIDKey)
|
||||
if err != nil || id <= 0 {
|
||||
// when not already logged in.
|
||||
c.Path = PathLogin
|
||||
return
|
||||
}
|
||||
|
||||
u, found := c.Source.GetByID(id)
|
||||
if !found {
|
||||
// if the session exists but for some reason the user doesn't exist in the "database"
|
||||
// then logout him and redirect to the register page.
|
||||
c.logout()
|
||||
return
|
||||
}
|
||||
|
||||
// set the model and render the view template.
|
||||
c.User = u
|
||||
c.Data["Title"] = "Profile of " + u.Username
|
||||
c.Tmpl = pathMyProfile + ".html"
|
||||
}
|
||||
|
||||
func (c *Controller) renderNotFound(id int64) {
|
||||
c.Status = 404
|
||||
c.Data["Title"] = "User Not Found"
|
||||
c.Data["ID"] = id
|
||||
c.Tmpl = "user/notfound.html"
|
||||
}
|
||||
|
||||
// GetBy handles GET:/user/{id:long},
|
||||
// i.e http://localhost:8080/user/1
|
||||
func (c *Controller) GetBy(userID int64) {
|
||||
// we have /user/{id}
|
||||
// fetch and render user json.
|
||||
if user, found := c.Source.GetByID(userID); !found {
|
||||
// not user found with that ID.
|
||||
c.renderNotFound(userID)
|
||||
} else {
|
||||
c.Ctx.JSON(user)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// IDGenerator would be our user ID generator
|
||||
// but here we keep the order of users by their IDs
|
||||
// so we will use numbers that can be easly written
|
||||
// to the browser to get results back from the REST API.
|
||||
// var IDGenerator = func() string {
|
||||
// return uuid.NewV4().String()
|
||||
// }
|
||||
|
||||
// DataSource is our data store example.
|
||||
type DataSource struct {
|
||||
Users map[int64]Model
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewDataSource returns a new user data source.
|
||||
func NewDataSource() *DataSource {
|
||||
return &DataSource{
|
||||
Users: make(map[int64]Model),
|
||||
}
|
||||
}
|
||||
|
||||
// GetBy receives a query function
|
||||
// which is fired for every single user model inside
|
||||
// our imaginary database.
|
||||
// When that function returns true then it stops the iteration.
|
||||
//
|
||||
// It returns the query's return last known boolean value
|
||||
// and the last known user model
|
||||
// to help callers to reduce the loc.
|
||||
//
|
||||
// But be carefully, the caller should always check for the "found"
|
||||
// because it may be false but the user model has actually real data inside it.
|
||||
//
|
||||
// It's actually a simple but very clever prototype function
|
||||
// I'm think of and using everywhere since then,
|
||||
// hope you find it very useful too.
|
||||
func (d *DataSource) GetBy(query func(Model) bool) (user Model, found bool) {
|
||||
d.mu.RLock()
|
||||
for _, user = range d.Users {
|
||||
found = query(user)
|
||||
if found {
|
||||
break
|
||||
}
|
||||
}
|
||||
d.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// GetByID returns a user model based on its ID.
|
||||
func (d *DataSource) GetByID(id int64) (Model, bool) {
|
||||
return d.GetBy(func(u Model) bool {
|
||||
return u.ID == id
|
||||
})
|
||||
}
|
||||
|
||||
// GetByUsername returns a user model based on the Username.
|
||||
func (d *DataSource) GetByUsername(username string) (Model, bool) {
|
||||
return d.GetBy(func(u Model) bool {
|
||||
return u.Username == username
|
||||
})
|
||||
}
|
||||
|
||||
func (d *DataSource) getLastID() (lastID int64) {
|
||||
d.mu.RLock()
|
||||
for id := range d.Users {
|
||||
if id > lastID {
|
||||
lastID = id
|
||||
}
|
||||
}
|
||||
d.mu.RUnlock()
|
||||
|
||||
return lastID
|
||||
}
|
||||
|
||||
// InsertOrUpdate adds or updates a user to the (memory) storage.
|
||||
func (d *DataSource) InsertOrUpdate(user Model) (Model, error) {
|
||||
// no matter what we will update the password hash
|
||||
// for both update and insert actions.
|
||||
hashedPassword, err := GeneratePassword(user.password)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
user.HashedPassword = hashedPassword
|
||||
|
||||
// update
|
||||
if id := user.ID; id > 0 {
|
||||
_, found := d.GetByID(id)
|
||||
if !found {
|
||||
return user, errors.New("ID should be zero or a valid one that maps to an existing User")
|
||||
}
|
||||
d.mu.Lock()
|
||||
d.Users[id] = user
|
||||
d.mu.Unlock()
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// insert
|
||||
id := d.getLastID() + 1
|
||||
user.ID = id
|
||||
d.mu.Lock()
|
||||
user.CreatedAt = time.Now()
|
||||
d.Users[id] = user
|
||||
d.mu.Unlock()
|
||||
|
||||
return user, nil
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// Model is our User example model.
|
||||
type Model struct {
|
||||
ID int64 `json:"id"`
|
||||
Firstname string `json:"firstname"`
|
||||
Username string `json:"username"`
|
||||
// password is the client-given password
|
||||
// which will not be stored anywhere in the server.
|
||||
// It's here only for actions like registration and update password,
|
||||
// because we caccept a Model instance
|
||||
// inside the `DataSource#InsertOrUpdate` function.
|
||||
password string
|
||||
HashedPassword []byte `json:"-"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
// GeneratePassword will generate a hashed password for us based on the
|
||||
// user's input.
|
||||
func GeneratePassword(userPassword string) ([]byte, error) {
|
||||
return bcrypt.GenerateFromPassword([]byte(userPassword), bcrypt.DefaultCost)
|
||||
}
|
||||
|
||||
// ValidatePassword will check if passwords are matched.
|
||||
func ValidatePassword(userPassword string, hashed []byte) (bool, error) {
|
||||
if err := bcrypt.CompareHashAndPassword(hashed, []byte(userPassword)); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<h1>Error.</h1>
|
||||
<h2>An error occurred while processing your request.</h2>
|
||||
|
||||
<h3>{{.Message}}</h3>
|
||||
@@ -0,0 +1,12 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>{{.Title}}</title>
|
||||
<link rel="stylesheet" type="text/css" href="/public/css/site.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{{ yield }}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,11 @@
|
||||
<form action="/user/login" method="POST">
|
||||
<div class="container">
|
||||
<label><b>Username</b></label>
|
||||
<input type="text" placeholder="Enter Username" name="username" required>
|
||||
|
||||
<label><b>Password</b></label>
|
||||
<input type="password" placeholder="Enter Password" name="password" required>
|
||||
|
||||
<button type="submit">Login</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -0,0 +1,3 @@
|
||||
<p>
|
||||
Welcome back <strong>{{.User.Firstname}}</strong>!
|
||||
</p>
|
||||
@@ -0,0 +1,3 @@
|
||||
<p>
|
||||
User with ID <strong>{{.ID}}</strong> does not exist.
|
||||
</p>
|
||||
@@ -0,0 +1,14 @@
|
||||
<form action="/user/register" method="POST">
|
||||
<div class="container">
|
||||
<label><b>Firstname</b></label>
|
||||
<input type="text" placeholder="Enter Firstname" name="firstname" required>
|
||||
|
||||
<label><b>Username</b></label>
|
||||
<input type="text" placeholder="Enter Username" name="username" required>
|
||||
|
||||
<label><b>Password</b></label>
|
||||
<input type="password" placeholder="Enter Password" name="password" required>
|
||||
|
||||
<button type="submit">Register</button>
|
||||
</div>
|
||||
</form>
|
||||
1
_examples/structuring/login-mvc/README.md
Normal file
1
_examples/structuring/login-mvc/README.md
Normal file
@@ -0,0 +1 @@
|
||||
# Please navigate to the [_examples/mvc/login](https://github.com/kataras/iris/tree/master/_examples/mvc/login)
|
||||
@@ -0,0 +1 @@
|
||||
# Please navigate to the [_examples/mvc/overview](https://github.com/kataras/iris/tree/master/_examples/mvc/overview)
|
||||
@@ -1,144 +0,0 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/securecookie"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/middleware/logger"
|
||||
"github.com/kataras/iris/middleware/recover"
|
||||
"github.com/kataras/iris/sessions"
|
||||
|
||||
"github.com/kataras/iris/_examples/structuring/mvc/app/controllers/follower"
|
||||
"github.com/kataras/iris/_examples/structuring/mvc/app/controllers/following"
|
||||
"github.com/kataras/iris/_examples/structuring/mvc/app/controllers/index"
|
||||
"github.com/kataras/iris/_examples/structuring/mvc/app/controllers/like"
|
||||
)
|
||||
|
||||
// Application is our application wrapper and bootstrapper, keeps our settings.
|
||||
type Application struct {
|
||||
*iris.Application
|
||||
|
||||
Name string
|
||||
Owner string
|
||||
SpawnDate time.Time
|
||||
|
||||
Sessions *sessions.Sessions
|
||||
}
|
||||
|
||||
// NewApplication returns a new named Application.
|
||||
func NewApplication(name, owner string) *Application {
|
||||
return &Application{
|
||||
Name: name,
|
||||
Owner: owner,
|
||||
Application: iris.New(),
|
||||
SpawnDate: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
// begin sends the app's identification info.
|
||||
func (app *Application) begin(ctx iris.Context) {
|
||||
// response headers
|
||||
ctx.Header("App-Name", app.Name)
|
||||
ctx.Header("App-Owner", app.Owner)
|
||||
ctx.Header("App-Since", time.Since(app.SpawnDate).String())
|
||||
|
||||
ctx.Header("Server", "Iris: https://iris-go.com")
|
||||
|
||||
// view data if ctx.View or c.Tmpl = "$page.html" will be called next.
|
||||
ctx.ViewData("AppName", app.Name)
|
||||
ctx.ViewData("AppOwner", app.Owner)
|
||||
ctx.Next()
|
||||
}
|
||||
|
||||
// SetupViews loads the templates.
|
||||
func (app *Application) SetupViews(viewsDir string) {
|
||||
app.RegisterView(iris.HTML(viewsDir, ".html").Layout("shared/layout.html"))
|
||||
}
|
||||
|
||||
// SetupSessions initializes the sessions, optionally.
|
||||
func (app *Application) SetupSessions(expires time.Duration, cookieHashKey, cookieBlockKey []byte) {
|
||||
app.Sessions = sessions.New(sessions.Config{
|
||||
Cookie: "SECRET_SESS_COOKIE_" + app.Name,
|
||||
Expires: expires,
|
||||
Encoding: securecookie.New(cookieHashKey, cookieBlockKey),
|
||||
})
|
||||
}
|
||||
|
||||
// SetupErrorHandlers prepares the http error handlers (>=400).
|
||||
// Remember that error handlers in Iris have their own middleware ecosystem
|
||||
// so the route's middlewares are not running when an http error happened.
|
||||
// So if we want a logger we have to re-create one, here we will customize that logger as well.
|
||||
func (app *Application) SetupErrorHandlers() {
|
||||
httpErrStatusLogger := logger.New(logger.Config{
|
||||
Status: true,
|
||||
IP: true,
|
||||
Method: true,
|
||||
Path: true,
|
||||
MessageContextKey: "message",
|
||||
LogFunc: func(now time.Time, latency time.Duration,
|
||||
status, ip, method, path string,
|
||||
message interface{}) {
|
||||
|
||||
line := fmt.Sprintf("%v %4v %s %s %s", status, latency, ip, method, path)
|
||||
|
||||
if message != nil {
|
||||
line += fmt.Sprintf(" %v", message)
|
||||
}
|
||||
app.Logger().Warn(line)
|
||||
},
|
||||
})
|
||||
|
||||
app.OnAnyErrorCode(app.begin, httpErrStatusLogger, func(ctx iris.Context) {
|
||||
err := iris.Map{
|
||||
"app": app.Name,
|
||||
"status": ctx.GetStatusCode(),
|
||||
"message": ctx.Values().GetString("message"),
|
||||
}
|
||||
|
||||
if jsonOutput, _ := ctx.URLParamBool("json"); jsonOutput {
|
||||
ctx.JSON(err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.ViewData("Err", err)
|
||||
ctx.ViewData("Title", "Error")
|
||||
ctx.View("shared/error.html")
|
||||
})
|
||||
}
|
||||
|
||||
// SetupRouter registers the available routes from the "controllers" package.
|
||||
func (app *Application) SetupRouter() {
|
||||
app.Use(recover.New())
|
||||
app.Use(app.begin)
|
||||
app.Use(iris.Gzip)
|
||||
|
||||
app.Favicon("./public/favicon.ico")
|
||||
app.StaticWeb("/public", "./public")
|
||||
|
||||
app.Use(logger.New())
|
||||
|
||||
app.Controller("/", new(index.Controller))
|
||||
app.Controller("/follower", new(follower.Controller))
|
||||
app.Controller("/following", new(following.Controller))
|
||||
app.Controller("/like", new(like.Controller))
|
||||
}
|
||||
|
||||
// Instance is our global application bootstrap instance.
|
||||
var Instance = NewApplication("My Awesome App", "kataras2006@hotmail.com")
|
||||
|
||||
// Boot starts our default instance appolication.
|
||||
func Boot(runner iris.Runner, configurators ...iris.Configurator) {
|
||||
Instance.SetupViews("./app/views")
|
||||
Instance.SetupSessions(24*time.Hour,
|
||||
[]byte("the-big-and-secret-fash-key-here"),
|
||||
[]byte("lot-secret-of-characters-big-too"),
|
||||
)
|
||||
|
||||
Instance.SetupErrorHandlers()
|
||||
Instance.SetupRouter()
|
||||
|
||||
Instance.Run(runner, configurators...)
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package follower
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris"
|
||||
)
|
||||
|
||||
type Controller struct {
|
||||
iris.Controller
|
||||
}
|
||||
|
||||
func (c *Controller) GetBy(id int64) {
|
||||
c.Ctx.Writef("from "+c.Route().Path()+" with ID: %d", id)
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package following
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris"
|
||||
)
|
||||
|
||||
type Controller struct {
|
||||
iris.Controller
|
||||
}
|
||||
|
||||
func (c *Controller) GetBy(id int64) {
|
||||
c.Ctx.Writef("from "+c.Route().Path()+" with ID: %d", id)
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package index
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris"
|
||||
)
|
||||
|
||||
type Controller struct {
|
||||
iris.Controller
|
||||
}
|
||||
|
||||
func (c *Controller) Get() {
|
||||
c.Data["Title"] = "Index"
|
||||
c.Tmpl = "index.html"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package like
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris"
|
||||
)
|
||||
|
||||
type Controller struct {
|
||||
iris.Controller
|
||||
}
|
||||
|
||||
func (c *Controller) GetBy(id int64) {
|
||||
c.Ctx.Writef("from "+c.Route().Path()+" with ID: %d", id)
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<h1>Welcome!!</h1>
|
||||
@@ -1,5 +0,0 @@
|
||||
<h1 class="text-danger">Error.</h1>
|
||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||
|
||||
<h3>{{.Err.status}}</h3>
|
||||
<h4>{{.Err.message}}</h4>
|
||||
@@ -1,23 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<title>{{.Title}} - {{.AppName}}</title>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<!-- Render the current template here -->
|
||||
{{ yield }}
|
||||
<hr />
|
||||
<footer>
|
||||
<p>© 2017 - {{.AppOwner}}</p>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,15 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris"
|
||||
|
||||
"github.com/kataras/iris/_examples/structuring/mvc/app"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// http://localhost:8080
|
||||
// http://localhost:8080/follower/42
|
||||
// http://localhost:8080/following/42
|
||||
// http://localhost:8080/like/42
|
||||
app.Boot(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed), iris.WithoutVersionChecker)
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB |
Reference in New Issue
Block a user