From 9f464310e3854da74a72c48e873b54d0fc43e5e8 Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Fri, 10 Aug 2018 00:23:17 +0300 Subject: [PATCH] Add one more example for dependency injection (HTTP Request-based) using our hero Former-commit-id: ff18371d254caf2d1932d6522b1ebc1f8032708f --- _examples/README.md | 15 +-- _examples/README_ZH.md | 2 + _examples/hero/smart-contract/main.go | 182 ++++++++++++++++++++++++++ 3 files changed, 188 insertions(+), 11 deletions(-) create mode 100644 _examples/hero/smart-contract/main.go diff --git a/_examples/README.md b/_examples/README.md index 084f1b50..26530a19 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -21,19 +21,9 @@ cd _examples && go get ./... go get github.com/iris-contrib/middleware/... go get github.com/betacraft/yaag/irisyaag go get github.com/markbates/goth/... -go get github.com/getsentry/raven-go/... -go get github.com/casbin/casbin -go get github.com/markbates/goth/... -go get github.com/aws/aws-sdk-go/... -go get github.com/getsentry/raven-go/... go get github.com/casbin/casbin go get github.com/aws/aws-sdk-go/... -go get github.com/prometheus/client_golang/... -go get github.com/didip/tollbooth -go get github.com/valyala/quicktemplate -go get github.com/shiyanhui/hero -go get github.com/go-xorm/xorm -go get github.com/nfnt/resize +go get github.com/getsentry/raven-go/... go get github.com/prometheus/client_golang/... go get github.com/didip/tollbooth go get github.com/valyala/quicktemplate @@ -44,6 +34,7 @@ go get github.com/dgrijalva/jwt-go go get github.com/newrelic/go-agent go get github.com/valyala/tcplisten go get github.com/kataras/bindata/cmd/bindata +go get github.com/jmespath/go-jmespath ``` @@ -169,6 +160,8 @@ Navigate through examples for a better understanding. - [Basic](hero/basic/main.go) - [Overview](hero/overview) +- [Sessions](hero/sessions) **NEW** +- [Yet another dependency injection example and good practises at general](hero/smart-contract/main.go) **NEW** ### MVC diff --git a/_examples/README_ZH.md b/_examples/README_ZH.md index 59c8dc74..3958a5d4 100644 --- a/_examples/README_ZH.md +++ b/_examples/README_ZH.md @@ -119,6 +119,8 @@ app.Get("{root:path}", rootWildcardHandler) - [基础](hero/basic/main.go) - [概览](hero/overview) +- [Sessions](hero/sessions) **NEW** +- [Yet another dependency injection example and good practises at general](hero/smart-contract/main.go) **NEW** ### MVC 模式 diff --git a/_examples/hero/smart-contract/main.go b/_examples/hero/smart-contract/main.go new file mode 100644 index 00000000..7a29e5ad --- /dev/null +++ b/_examples/hero/smart-contract/main.go @@ -0,0 +1,182 @@ +package main + +import ( + "fmt" + "strings" + + "github.com/kataras/iris" + "github.com/kataras/iris/context" + "github.com/kataras/iris/hero" + + // External package to optionally filter JSON responses before sent, + // see `sendJSON` for more. + "github.com/jmespath/go-jmespath" +) + +/* + $ go get github.com/jmespath/go-jmespath +*/ + +func newApp() *iris.Application { + app := iris.New() + + // PartyFunc is the same as usersRouter := app.Party("/users") + // but it gives us an easy way to call router's registration functions, + // i.e functions from another package that can handle this group of routes. + app.PartyFunc("/users", registerUsersRoutes) + + return app +} + +func main() { + app := newApp() + + // http://localhost:8080/users?query=[?Name == 'John Doe'].Age + // <- client will receive the age of a user which his name is "John Doe". + // You can also test query=[0].Name to retrieve the first user's name. + // Or even query=[0:3].Age to print the first three ages. + // Learn more about jmespath and how to filter: + // http://jmespath.readthedocs.io/en/latest/ and + // https://github.com/jmespath/go-jmespath/tree/master/fuzz/testdata + // + // http://localhost:8080/users + // http://localhost:8080/users/William%20Woe + // http://localhost:8080/users/William%20Woe/age + app.Run(iris.Addr(":8080")) +} + +/* + START OF USERS ROUTER +*/ + +func registerUsersRoutes(usersRouter iris.Party) { + // GET: /users + usersRouter.Get("/", getAllUsersHandler) + usersRouter.PartyFunc("/{name:string}", registerUserRoutes) +} + +type user struct { + Name string `json:"name"` + Age int `json:"age"` +} + +var usersSample = []*user{ + {"William Woe", 25}, + {"Mary Moe", 15}, + {"John Doe", 17}, +} + +func getAllUsersHandler(ctx iris.Context) { + err := sendJSON(ctx, usersSample) + if err != nil { + fail(ctx, iris.StatusInternalServerError, "unable to send a list of all users: %v", err) + return + } +} + +/* + START OF USERS.USER SUB ROUTER +*/ + +func registerUserRoutes(userRouter iris.Party) { + // create a new dependency injection manager for this sub router. + userDeps := hero.New() + // you can also use the global/package-level hero.Register(userDependency) as we have already learned in other examples. + userDeps.Register(userDependency) + + // GET: /users/{name:string} + userRouter.Get("/", userDeps.Handler(getUserHandler)) + // GET: /users/{name:string}/age + userRouter.Get("/age", userDeps.Handler(getUserAgeHandler)) +} + +var userDependency = func(ctx iris.Context) *user { + name := strings.Title(ctx.Params().Get("name")) + for _, u := range usersSample { + if u.Name == name { + return u + } + } + + // you may want or no to handle the error here, either way the main route handler + // is going to be executed, always. A dynamic dependency(per-request) is not a middleware, so things like `ctx.Next()` or `ctx.StopExecution()` + // do not apply here, look the `getUserHandler`'s first lines; we stop/exit the handler manually + // if the received user is nil but depending on your app's needs, it is possible to do other things too. + // A dynamic dependency like this can return more output values, i.e (*user, bool). + fail(ctx, iris.StatusNotFound, "user with name '%s' not found", name) + return nil +} + +func getUserHandler(ctx iris.Context, u *user) { + if u == nil { + return + } + + sendJSON(ctx, u) +} + +func getUserAgeHandler(ctx iris.Context, u *user) { + if u == nil { + return + } + + ctx.Writef("%d", u.Age) +} + +/* Remember, with 'hero' you get mvc-like functions, so this can work too: +func getUserAgeHandler(u *user) string { + if u == nil { + return "" + } + + return fmt.Sprintf("%d", u.Age) +} +*/ + +/* END OF USERS.USER SUB ROUTER */ + +/* END OF USERS ROUTER */ + +// common JSON response for manual HTTP errors, optionally. +type httpError struct { + Code int `json:"code"` + Reason string `json:"reason"` +} + +func (h httpError) Error() string { + return fmt.Sprintf("Status Code: %d\nReason: %s", h.Code, h.Reason) +} + +func fail(ctx context.Context, statusCode int, format string, a ...interface{}) { + err := httpError{ + Code: statusCode, + Reason: fmt.Sprintf(format, a...), + } + + // log all the >= 500 internal errors. + if statusCode >= 500 { + ctx.Application().Logger().Error(err) + } + + ctx.StatusCode(statusCode) + ctx.JSON(err) + + // no next handlers will run. + ctx.StopExecution() +} + +// JSON helper to give end-user the ability to put indention chars or filtering the response, you can do that, optionally. +// If you'd like to see that function inside the Iris' Context itself raise a [Feature Request] issue and link this example. +func sendJSON(ctx iris.Context, resp interface{}) (err error) { + indent := ctx.URLParamDefault("indent", " ") + // i.e [?Name == 'John Doe'].Age # to output the [age] of a user which his name is "John Doe". + if query := ctx.URLParam("query"); query != "" && query != "[]" { + resp, err = jmespath.Search(query, resp) + if err != nil { + return + } + } + + _, err = ctx.JSON(resp, context.JSON{Indent: indent, UnescapeHTML: true}) + return err +}