diff --git a/_examples/README.md b/_examples/README.md index e0aa242e..7d5f71d5 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -22,6 +22,7 @@ Structuring is always depends on your needs. We can't tell you how to design you - [Example 1](mvc/login) - [Example 2](structuring/mvc) +- [Example 3](structuring/handler-based) ### HTTP Listening diff --git a/_examples/structuring/handler-based/bootstrap/bootstrapper.go b/_examples/structuring/handler-based/bootstrap/bootstrapper.go new file mode 100644 index 00000000..3257b9a0 --- /dev/null +++ b/_examples/structuring/handler-based/bootstrap/bootstrapper.go @@ -0,0 +1,113 @@ +package bootstrap + +import ( + "time" + + "github.com/gorilla/securecookie" + + "github.com/kataras/iris" + "github.com/kataras/iris/sessions" + "github.com/kataras/iris/websocket" +) + +type Configurator func(*Bootstrapper) + +type Bootstrapper struct { + *iris.Application + AppName string + AppOwner string + AppSpawnDate time.Time + + Sessions *sessions.Sessions +} + +// New returns a new Bootstrapper. +func New(appName, appOwner string, cfgs ...Configurator) *Bootstrapper { + b := &Bootstrapper{ + AppName: appName, + AppOwner: appOwner, + AppSpawnDate: time.Now(), + Application: iris.New(), + } + + for _, cfg := range cfgs { + cfg(b) + } + + return b +} + +// SetupViews loads the templates. +func (b *Bootstrapper) SetupViews(viewsDir string) { + b.RegisterView(iris.HTML(viewsDir, ".html").Layout("shared/layout.html")) +} + +// SetupSessions initializes the sessions, optionally. +func (b *Bootstrapper) SetupSessions(expires time.Duration, cookieHashKey, cookieBlockKey []byte) { + b.Sessions = sessions.New(sessions.Config{ + Cookie: "SECRET_SESS_COOKIE_" + b.AppName, + Expires: expires, + Encoding: securecookie.New(cookieHashKey, cookieBlockKey), + }) +} + +// SetupWebsockets prepares the websocket server. +func (b *Bootstrapper) SetupWebsockets(endpoint string, onConnection websocket.ConnectionFunc) { + ws := websocket.New(websocket.Config{}) + ws.OnConnection(onConnection) + + b.Get(endpoint, ws.Handler()) + b.Any("/iris-ws.js", func(ctx iris.Context) { + ctx.Write(websocket.ClientSource) + }) +} + +// SetupErrorHandlers prepares the http error handlers (>=400). +func (b *Bootstrapper) SetupErrorHandlers() { + b.OnAnyErrorCode(func(ctx iris.Context) { + err := iris.Map{ + "app": b.AppName, + "status": ctx.GetStatusCode(), + "message": ctx.Values().GetString("message"), + } + + if jsonOutput := ctx.URLParamExists("json"); jsonOutput { + ctx.JSON(err) + return + } + + ctx.ViewData("Err", err) + ctx.ViewData("Title", "Error") + ctx.View("shared/error.html") + }) +} + +const ( + // StaticAssets is the root directory for public assets like images, css, js. + StaticAssets = "./public/" + // Favicon is the relative 9to the "StaticAssets") favicon path for our app. + Favicon = "favicon.ico" +) + +// Bootstrap prepares our application. +// +// Returns itself. +func (b *Bootstrapper) Bootstrap() *Bootstrapper { + b.SetupViews("./views") + b.SetupSessions(24*time.Hour, + []byte("the-big-and-secret-fash-key-here"), + []byte("lot-secret-of-characters-big-too"), + ) + b.SetupErrorHandlers() + + // static files + b.Favicon(StaticAssets + Favicon) + b.StaticWeb(StaticAssets[1:len(StaticAssets)-1], StaticAssets) + + return b +} + +// Listen starts the http server with the specified "addr". +func (b *Bootstrapper) Listen(addr string, cfgs ...iris.Configurator) { + b.Run(iris.Addr(addr), cfgs...) +} diff --git a/_examples/structuring/handler-based/main.go b/_examples/structuring/handler-based/main.go new file mode 100644 index 00000000..06704fc5 --- /dev/null +++ b/_examples/structuring/handler-based/main.go @@ -0,0 +1,20 @@ +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") +} diff --git a/_examples/structuring/handler-based/main_test.go b/_examples/structuring/handler-based/main_test.go new file mode 100644 index 00000000..ba51a081 --- /dev/null +++ b/_examples/structuring/handler-based/main_test.go @@ -0,0 +1,31 @@ +package main + +import ( + "testing" + + "github.com/kataras/iris/httptest" +) + +// TestApp runs by `$ go test -v`. +func TestApp(t *testing.T) { + e := httptest.New(t, app.Application) + + // test our routes + e.GET("/").Expect().Status(httptest.StatusOK) + e.GET("/follower/42").Expect().Status(httptest.StatusOK). + Body().Equal("from /follower/{id:long} with ID: 42") + e.GET("/following/52").Expect().Status(httptest.StatusOK). + Body().Equal("from /following/{id:long} with ID: 52") + e.GET("/like/64").Expect().Status(httptest.StatusOK). + Body().Equal("from /like/{id:long} with ID: 64") + + // test not found + e.GET("/notfound").Expect().Status(httptest.StatusNotFound) + expectedErr := map[string]interface{}{ + "app": app.AppName, + "status": httptest.StatusNotFound, + "message": "", + } + e.GET("/anotfoundwithjson").WithQuery("json", nil). + Expect().Status(httptest.StatusNotFound).JSON().Equal(expectedErr) +} diff --git a/_examples/structuring/handler-based/middleware/identity/identity.go b/_examples/structuring/handler-based/middleware/identity/identity.go new file mode 100644 index 00000000..496d0de8 --- /dev/null +++ b/_examples/structuring/handler-based/middleware/identity/identity.go @@ -0,0 +1,33 @@ +package identity + +import ( + "time" + + "github.com/kataras/iris" + + "github.com/kataras/iris/_examples/structuring/handler-based/bootstrap" +) + +// New returns a new handler which adds some headers and view data +// describing the application, i.e the owner, the startup time. +func New(b *bootstrap.Bootstrapper) iris.Handler { + return func(ctx iris.Context) { + // response headers + ctx.Header("App-Name", b.AppName) + ctx.Header("App-Owner", b.AppOwner) + ctx.Header("App-Since", time.Since(b.AppSpawnDate).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", b.AppName) + ctx.ViewData("AppOwner", b.AppOwner) + ctx.Next() + } +} + +// Configure creates a new identity middleware and registers that to the app. +func Configure(b *bootstrap.Bootstrapper) { + h := New(b) + b.UseGlobal(h) +} diff --git a/_examples/structuring/handler-based/public/favicon.ico b/_examples/structuring/handler-based/public/favicon.ico new file mode 100644 index 00000000..c370da51 Binary files /dev/null and b/_examples/structuring/handler-based/public/favicon.ico differ diff --git a/_examples/structuring/handler-based/routes/follower.go b/_examples/structuring/handler-based/routes/follower.go new file mode 100644 index 00000000..3dd0f012 --- /dev/null +++ b/_examples/structuring/handler-based/routes/follower.go @@ -0,0 +1,11 @@ +package routes + +import ( + "github.com/kataras/iris" +) + +// GetFollowerHandler handles the GET: /follower/{id} +func GetFollowerHandler(ctx iris.Context) { + id, _ := ctx.Params().GetInt64("id") + ctx.Writef("from "+ctx.GetCurrentRoute().Path()+" with ID: %d", id) +} diff --git a/_examples/structuring/handler-based/routes/following.go b/_examples/structuring/handler-based/routes/following.go new file mode 100644 index 00000000..e81c9c24 --- /dev/null +++ b/_examples/structuring/handler-based/routes/following.go @@ -0,0 +1,11 @@ +package routes + +import ( + "github.com/kataras/iris" +) + +// GetFollowingHandler handles the GET: /following/{id} +func GetFollowingHandler(ctx iris.Context) { + id, _ := ctx.Params().GetInt64("id") + ctx.Writef("from "+ctx.GetCurrentRoute().Path()+" with ID: %d", id) +} diff --git a/_examples/structuring/handler-based/routes/index.go b/_examples/structuring/handler-based/routes/index.go new file mode 100644 index 00000000..18541ed0 --- /dev/null +++ b/_examples/structuring/handler-based/routes/index.go @@ -0,0 +1,11 @@ +package routes + +import ( + "github.com/kataras/iris" +) + +// GetIndexHandler handles the GET: / +func GetIndexHandler(ctx iris.Context) { + ctx.ViewData("Title", "Index Page") + ctx.View("index.html") +} diff --git a/_examples/structuring/handler-based/routes/like.go b/_examples/structuring/handler-based/routes/like.go new file mode 100644 index 00000000..631653c4 --- /dev/null +++ b/_examples/structuring/handler-based/routes/like.go @@ -0,0 +1,11 @@ +package routes + +import ( + "github.com/kataras/iris" +) + +// GetLikeHandler handles the GET: /like/{id} +func GetLikeHandler(ctx iris.Context) { + id, _ := ctx.Params().GetInt64("id") + ctx.Writef("from "+ctx.GetCurrentRoute().Path()+" with ID: %d", id) +} diff --git a/_examples/structuring/handler-based/routes/routes.go b/_examples/structuring/handler-based/routes/routes.go new file mode 100644 index 00000000..555acd12 --- /dev/null +++ b/_examples/structuring/handler-based/routes/routes.go @@ -0,0 +1,14 @@ +package routes + +import ( + "github.com/kataras/iris/_examples/structuring/handler-based/bootstrap" +) + +// Configure registers the nessecary routes to the app. +func Configure(b *bootstrap.Bootstrapper) { + // routes + b.Get("/", GetIndexHandler) + b.Get("/follower/{id:long}", GetFollowerHandler) + b.Get("/following/{id:long}", GetFollowingHandler) + b.Get("/like/{id:long}", GetLikeHandler) +} diff --git a/_examples/structuring/handler-based/views/index.html b/_examples/structuring/handler-based/views/index.html new file mode 100644 index 00000000..4a813af6 --- /dev/null +++ b/_examples/structuring/handler-based/views/index.html @@ -0,0 +1 @@ +