mirror of
https://github.com/kataras/iris.git
synced 2025-02-13 04:26:18 +01:00
MVC Example - you can learn a lot | Asleep for your eyes (again)
Former-commit-id: cd66c00b29c903b724763cb84cae96426934acb5
This commit is contained in:
parent
39a24fb7cb
commit
2786ca1960
|
@ -47,6 +47,7 @@ Iris is a fast, simple and efficient micro web framework for Go. It provides a b
|
||||||
* [Tutorial: Online Visitors](_examples/tutorial/online-visitors)
|
* [Tutorial: Online Visitors](_examples/tutorial/online-visitors)
|
||||||
* [Tutorial: URL Shortener using BoltDB](https://medium.com/@kataras/a-url-shortener-service-using-go-iris-and-bolt-4182f0b00ae7)
|
* [Tutorial: URL Shortener using BoltDB](https://medium.com/@kataras/a-url-shortener-service-using-go-iris-and-bolt-4182f0b00ae7)
|
||||||
* [Tutorial: How to turn your Android Device into a fully featured Web Server (**MUST**)](https://twitter.com/ThePracticalDev/status/892022594031017988)
|
* [Tutorial: How to turn your Android Device into a fully featured Web Server (**MUST**)](https://twitter.com/ThePracticalDev/status/892022594031017988)
|
||||||
|
* [Tutorial: Controllers from scratch (**Coming soon as built'n feature, probably at v8.3**)](_examples/tutorial/mvc)
|
||||||
* [POC: Convert the medium-sized project "Parrot" from native to Iris](https://github.com/iris-contrib/parrot)
|
* [POC: Convert the medium-sized project "Parrot" from native to Iris](https://github.com/iris-contrib/parrot)
|
||||||
* [Middleware](middleware/)
|
* [Middleware](middleware/)
|
||||||
* [Dockerize](https://github.com/iris-contrib/cloud-native-go)
|
* [Dockerize](https://github.com/iris-contrib/cloud-native-go)
|
||||||
|
|
|
@ -12,7 +12,8 @@ It doesn't always contain the "best ways" but it does cover each important featu
|
||||||
- [Glimpse](overview/main.go)
|
- [Glimpse](overview/main.go)
|
||||||
- [Tutorial: Online Visitors](tutorial/online-visitors/main.go)
|
- [Tutorial: Online Visitors](tutorial/online-visitors/main.go)
|
||||||
- [Tutorial: URL Shortener using BoltDB](https://medium.com/@kataras/a-url-shortener-service-using-go-iris-and-bolt-4182f0b00ae7)
|
- [Tutorial: URL Shortener using BoltDB](https://medium.com/@kataras/a-url-shortener-service-using-go-iris-and-bolt-4182f0b00ae7)
|
||||||
- [Tutorial: How to turn your Android Device into a fully featured Web Server](https://medium.com/@kataras/how-to-turn-an-android-device-into-a-web-server-9816b28ab199)
|
- [Tutorial: How to turn your Android Device into a fully featured Web Server (**MUST**)](https://twitter.com/ThePracticalDev/status/892022594031017988)
|
||||||
|
- [Tutorial: Controllers from scratch (**Coming soon as built'n feature, probably at v8.3**)](tutorial/mvc)
|
||||||
|
|
||||||
### HTTP Listening
|
### HTTP Listening
|
||||||
|
|
||||||
|
@ -193,6 +194,7 @@ iris cache library lives on its own [package](https://github.com/kataras/iris/tr
|
||||||
> You're free to use your own favourite caching package if you'd like so.
|
> You're free to use your own favourite caching package if you'd like so.
|
||||||
|
|
||||||
### Sessions
|
### Sessions
|
||||||
|
|
||||||
iris session manager lives on its own [package](https://github.com/kataras/iris/tree/master/sessions).
|
iris session manager lives on its own [package](https://github.com/kataras/iris/tree/master/sessions).
|
||||||
|
|
||||||
- [Overview](sessions/overview/main.go)
|
- [Overview](sessions/overview/main.go)
|
||||||
|
|
162
_examples/tutorial/mvc/controllers/controller.go
Normal file
162
_examples/tutorial/mvc/controllers/controller.go
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
// "unsafe"
|
||||||
|
|
||||||
|
"github.com/kataras/iris"
|
||||||
|
"github.com/kataras/iris/context"
|
||||||
|
"github.com/kataras/iris/core/router"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Controller struct {
|
||||||
|
// path params.
|
||||||
|
Params *context.RequestParams
|
||||||
|
|
||||||
|
// view properties.
|
||||||
|
Layout string
|
||||||
|
Tmpl string
|
||||||
|
Data map[string]interface{}
|
||||||
|
|
||||||
|
// give access to the request context itself.
|
||||||
|
Ctx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
// all lowercase, so user can see only the fields
|
||||||
|
// that are necessary to him/her.
|
||||||
|
func (b *Controller) init(ctx context.Context) {
|
||||||
|
b.Ctx = ctx
|
||||||
|
b.Params = ctx.Params()
|
||||||
|
b.Data = make(map[string]interface{}, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Controller) exec() {
|
||||||
|
if v := b.Tmpl; v != "" {
|
||||||
|
if l := b.Layout; l != "" {
|
||||||
|
b.Ctx.ViewLayout(l)
|
||||||
|
}
|
||||||
|
if d := b.Data; d != nil {
|
||||||
|
for key, value := range d {
|
||||||
|
b.Ctx.ViewData(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.Ctx.View(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the field name at compile-time,
|
||||||
|
// will help us to catch any unexpected results on future versions.
|
||||||
|
var baseControllerName = reflect.TypeOf(Controller{}).Name()
|
||||||
|
|
||||||
|
func RegisterController(app *iris.Application, path string, c interface{}) {
|
||||||
|
typ := reflect.TypeOf(c)
|
||||||
|
|
||||||
|
if typ.Kind() != reflect.Ptr {
|
||||||
|
typ = reflect.PtrTo(typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
elem := typ.Elem()
|
||||||
|
|
||||||
|
// check if "c" has the "Controller" typeof `Controller` field.
|
||||||
|
b, has := elem.FieldByName(baseControllerName)
|
||||||
|
if !has {
|
||||||
|
panic("controller should have a field of Controller type")
|
||||||
|
}
|
||||||
|
|
||||||
|
baseControllerFieldIndex := b.Index[0]
|
||||||
|
persistenceFields := make(map[int]reflect.Value, 0)
|
||||||
|
|
||||||
|
if numField := elem.NumField(); numField > 1 {
|
||||||
|
val := reflect.Indirect(reflect.ValueOf(c))
|
||||||
|
|
||||||
|
for i := 0; i < numField; i++ {
|
||||||
|
f := elem.Field(i)
|
||||||
|
valF := val.Field(i)
|
||||||
|
// catch persistence data by tags, i.e:
|
||||||
|
// MyData string `iris:"persistence"`
|
||||||
|
if t, ok := f.Tag.Lookup("iris"); ok {
|
||||||
|
if t == "persistence" {
|
||||||
|
persistenceFields[i] = reflect.ValueOf(val.Field(i).Interface())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// catch persistence data by pointer, i.e:
|
||||||
|
// DB *Database
|
||||||
|
if f.Type.Kind() == reflect.Ptr {
|
||||||
|
if !valF.IsNil() {
|
||||||
|
persistenceFields[i] = reflect.ValueOf(val.Field(i).Interface())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if has Any() or All()
|
||||||
|
// if yes, then register all http methods and
|
||||||
|
// exit.
|
||||||
|
m, has := typ.MethodByName("Any")
|
||||||
|
if !has {
|
||||||
|
m, has = typ.MethodByName("All")
|
||||||
|
}
|
||||||
|
if has {
|
||||||
|
app.Any(path,
|
||||||
|
controllerToHandler(elem, persistenceFields,
|
||||||
|
baseControllerFieldIndex, m.Index))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// else search the entire controller
|
||||||
|
// for any compatible method function
|
||||||
|
// and register that.
|
||||||
|
for _, method := range router.AllMethods {
|
||||||
|
httpMethodFuncName := strings.Title(strings.ToLower(method))
|
||||||
|
|
||||||
|
m, has := typ.MethodByName(httpMethodFuncName)
|
||||||
|
if !has {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
httpMethodIndex := m.Index
|
||||||
|
|
||||||
|
app.Handle(method, path,
|
||||||
|
controllerToHandler(elem, persistenceFields,
|
||||||
|
baseControllerFieldIndex, httpMethodIndex))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func controllerToHandler(elem reflect.Type, persistenceFields map[int]reflect.Value,
|
||||||
|
baseControllerFieldIndex, httpMethodIndex int) context.Handler {
|
||||||
|
return func(ctx context.Context) {
|
||||||
|
// create a new controller instance of that type(>ptr).
|
||||||
|
c := reflect.New(elem)
|
||||||
|
|
||||||
|
// get the responsible method.
|
||||||
|
// Remember:
|
||||||
|
// To improve the performance
|
||||||
|
// we don't compare the ctx.Method()[HTTP Method]
|
||||||
|
// to the instance's Method, each handler is registered
|
||||||
|
// to a specific http method.
|
||||||
|
methodFunc := c.Method(httpMethodIndex)
|
||||||
|
|
||||||
|
// get the Controller embedded field.
|
||||||
|
b, _ := c.Elem().Field(baseControllerFieldIndex).Addr().Interface().(*Controller)
|
||||||
|
|
||||||
|
if len(persistenceFields) > 0 {
|
||||||
|
elem := c.Elem()
|
||||||
|
for index, value := range persistenceFields {
|
||||||
|
elem.Field(index).Set(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// init the new controller instance.
|
||||||
|
b.init(ctx)
|
||||||
|
|
||||||
|
// execute the responsible method for that handler.
|
||||||
|
methodFunc.Interface().(func())()
|
||||||
|
|
||||||
|
// finally, execute the controller.
|
||||||
|
b.exec()
|
||||||
|
}
|
||||||
|
}
|
12
_examples/tutorial/mvc/controllers/index.go
Normal file
12
_examples/tutorial/mvc/controllers/index.go
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package controllers
|
||||||
|
|
||||||
|
// Index is our index example controller.
|
||||||
|
type Index struct {
|
||||||
|
Controller
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Index) Get() {
|
||||||
|
c.Tmpl = "index.html"
|
||||||
|
c.Data["title"] = "Index page"
|
||||||
|
c.Data["message"] = "Hello world!"
|
||||||
|
}
|
61
_examples/tutorial/mvc/controllers/user.go
Normal file
61
_examples/tutorial/mvc/controllers/user.go
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/kataras/iris/_examples/tutorial/mvc/persistence"
|
||||||
|
)
|
||||||
|
|
||||||
|
// User is our user example controller.
|
||||||
|
type User struct {
|
||||||
|
Controller
|
||||||
|
|
||||||
|
// all fields with pointers(*)
|
||||||
|
// that are not nil
|
||||||
|
// and all fields with
|
||||||
|
// that are tagged with iris:"persistence"`
|
||||||
|
// are being persistence and kept
|
||||||
|
// between the requests, meaning that
|
||||||
|
// they will not be reset-ed on each new request,
|
||||||
|
// they will be the same for all requests.
|
||||||
|
CreatedAt time.Time `iris:"persistence"`
|
||||||
|
Title string `iris:"persistence"`
|
||||||
|
DB *persistence.Database
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserController(db *persistence.Database) *User {
|
||||||
|
return &User{
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
Title: "User page",
|
||||||
|
DB: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get serves using the User controller when HTTP Method is "GET".
|
||||||
|
func (c *User) Get() {
|
||||||
|
c.Tmpl = "user/index.html"
|
||||||
|
c.Data["title"] = c.Title
|
||||||
|
c.Data["username"] = "kataras " + c.Params.Get("userid")
|
||||||
|
c.Data["connstring"] = c.DB.Connstring
|
||||||
|
c.Data["uptime"] = time.Now().Sub(c.CreatedAt).Seconds()
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Can use more than one, the factory will make sure
|
||||||
|
that the correct http methods are being registed for this
|
||||||
|
controller, uncommend these if you want:
|
||||||
|
|
||||||
|
func (c *User) Post() {}
|
||||||
|
func (c *User) Put() {}
|
||||||
|
func (c *User) Delete() {}
|
||||||
|
func (c *User) Connect() {}
|
||||||
|
func (c *User) Head() {}
|
||||||
|
func (c *User) Patch() {}
|
||||||
|
func (c *User) Options() {}
|
||||||
|
func (c *User) Trace() {}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
func (c *User) All() {}
|
||||||
|
// OR
|
||||||
|
func (c *User) Any() {}
|
||||||
|
*/
|
24
_examples/tutorial/mvc/main.go
Normal file
24
_examples/tutorial/mvc/main.go
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kataras/iris/_examples/tutorial/mvc/controllers"
|
||||||
|
"github.com/kataras/iris/_examples/tutorial/mvc/persistence"
|
||||||
|
|
||||||
|
"github.com/kataras/iris"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app := iris.New()
|
||||||
|
app.RegisterView(iris.HTML("./views", ".html"))
|
||||||
|
|
||||||
|
db := persistence.OpenDatabase("a fake db")
|
||||||
|
|
||||||
|
controllers.RegisterController(app, "/", new(controllers.Index))
|
||||||
|
|
||||||
|
controllers.RegisterController(app, "/user/{userid:int}",
|
||||||
|
controllers.NewUserController(db))
|
||||||
|
|
||||||
|
// http://localhost/
|
||||||
|
// http://localhost:8080/user/42
|
||||||
|
app.Run(iris.Addr(":8080"))
|
||||||
|
}
|
15
_examples/tutorial/mvc/models/user.go
Normal file
15
_examples/tutorial/mvc/models/user.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// User is an example model.
|
||||||
|
type User struct {
|
||||||
|
ID int64
|
||||||
|
Username string
|
||||||
|
Firstname string
|
||||||
|
Lastname string
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
}
|
10
_examples/tutorial/mvc/persistence/database.go
Normal file
10
_examples/tutorial/mvc/persistence/database.go
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package persistence
|
||||||
|
|
||||||
|
// Database is our imaginary storage.
|
||||||
|
type Database struct {
|
||||||
|
Connstring string
|
||||||
|
}
|
||||||
|
|
||||||
|
func OpenDatabase(connstring string) *Database {
|
||||||
|
return &Database{Connstring: connstring}
|
||||||
|
}
|
11
_examples/tutorial/mvc/views/index.html
Normal file
11
_examples/tutorial/mvc/views/index.html
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>{{.title}}</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>{{.message}}</h1>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
17
_examples/tutorial/mvc/views/user/index.html
Normal file
17
_examples/tutorial/mvc/views/user/index.html
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>{{.title}}</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1> Hello {{.username}} </h1>
|
||||||
|
|
||||||
|
|
||||||
|
All fields inside a controller that are pointers or they tagged as `iris:"persistence"` are marked as persistence data, meaning
|
||||||
|
that they will not be reset-ed on each new request.
|
||||||
|
<h3>Persistence data from *DB.Connstring: {{.connstring}} </h3>
|
||||||
|
<h3>Persistence data from CreatedAt `iris:"persistence"`: {{.uptime}} seconds </h3>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -80,6 +80,15 @@ func TestRouterWildcardAndStatic(t *testing.T) {
|
||||||
{"GET", "/some/static", h, []testRouteRequest{
|
{"GET", "/some/static", h, []testRouteRequest{
|
||||||
{"GET", "", "/some/static", iris.StatusOK, same_as_request_path},
|
{"GET", "", "/some/static", iris.StatusOK, same_as_request_path},
|
||||||
}},
|
}},
|
||||||
|
|
||||||
|
{"GET", "/s/{p:path}", h2, []testRouteRequest{
|
||||||
|
{"GET", "", "/s/that/is/wildcard", iris.StatusForbidden, same_as_request_path},
|
||||||
|
{"GET", "", "/s/did", iris.StatusForbidden, same_as_request_path},
|
||||||
|
{"GET", "", "/s1/that/is/wildcard", iris.StatusNotFound, from_status_code},
|
||||||
|
}},
|
||||||
|
{"GET", "/s/static", h, []testRouteRequest{
|
||||||
|
{"GET", "", "/s/static", iris.StatusOK, same_as_request_path},
|
||||||
|
}},
|
||||||
}
|
}
|
||||||
|
|
||||||
testTheRoutes(t, tt, false)
|
testTheRoutes(t, tt, false)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user