Consider the following example:
// application/app.go
package application
type Application struct {
Server inner.Server `json:"server"`
Database inner.Database `json:"database"`
}
var App Application
func (app *Application) Build(path string) {
// read a json file and populate App ...
}
// main.go
package main
func main() {
application.App.Build("path/to/config.json")
e := echo.New()
// Is it possible to attach "application.App" to "e" and pass it via "echo.Context" to handler functions?
e.GET("/", controller.IndexGet)
e.Run(fasthttp.New(s.HttpAddress))
}
// controller/index.go
package controller
func IndexGet(c echo.Context) error {
// Here I need to access application.App.Database which makes "import cycle not allowed" error
db := application.App.Database.New()
}
How can I attach application.App to the e in main.go and access it via echo.Context of handler functions in index.go?
You can use Context#Set and Context#Get to store/retrieve arbitrary objects.
Thank you @vishr, I used the following code:
e := echo.New()
e.GetContext().Set("app", application.App)
e.GET("/", controller.IndexGet)
but I got nil when I used c.Get("app") in IndexGet.
Finally, it was solved using middlewares:
func main() {
application.App.Build("path/to/config.json")
e := echo.New()
e.Use(bindApp())
e.GET("/", controller.IndexGet)
e.Run(fasthttp.New(s.HttpAddress))
}
func bindApp() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
c.Set("app", application.App)
return next(c)
}
}
}
Nice!
The disadvantage of using middleware is that the Set will call when every request is called.
Server config should be initialized on server start and fail-fast if there is an error in connecting to the DB
The disadvantage of using middleware is that the
Setwill call when every request is called.
is there a way to avoid this?
The disadvantage of using middleware is that the
Setwill call when every request is called.is there a way to avoid this?
There is a work around
*echo.Echo by reference// 'e' is of type *echo.Echo
// "client" is the item you wish to pass (could be anything)
InjectAddressController(e, client)
func InjectAddressController(echo *echo.Echo, client *mongo.Client) {
echo.POST("/address", func(ctx echo.Context) error {
return ctx.Render(http.StatusOK, "index.html", map[string]interface{}{
"name": "Dolly!",
})
})
}
Im not so sure about what I said regarding "pass by reference" take a look here https://dave.cheney.net/2017/04/29/there-is-no-pass-by-reference-in-go
Hi,
I just rewrote your latest proposal to make it a little bit more generic (the call to echo.POST confused me)
I agree with you, instead of defining handler as per Echo documentation (func foo (c echo.Context)...), we can wrap it into a upper level function that returns the handler itself. With such option, we can pass anything we want to the handler without having to use the echo context:
// controller.go
package controller
// IndexGet in not anymore our echo Handler...
func IndexGet(app *Application) echo.HandlerFunc {
// ... but it returns a echo handler
return func(c echo.Context) error {
// Do any stuff here, including usage of app
}
}
```go
// main.go
package main
func main() {
application.App.Build("path/to/config.json")
// Handle here the case where your AppBuild crashes, cause I guess you don't need your server anymore
// Instantiate an Echo server
e := echo.New()
// Define your routes
e.GET("/", controller.IndexGet(app))
e.Run(fasthttp.New(s.HttpAddress))
}
Why not bind IndexGet to your application instance?
e.GET("/", app.IndexGet)
// ...
func (app *application) IndexGet(c echo.Context) error {
// use app.Database or whatever
return c.String(http.StatusOK, "Hello, World!")
}
The disadvantage of using middleware is that the
Setwill call when every request is called.
whats the cons of calling Set ? performance issueS ?
Most helpful comment
Finally, it was solved using middlewares: