Echo: How to attach a custom interface{} to the echo.Context

Created on 19 May 2016  路  12Comments  路  Source: labstack/echo

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?

question

Most helpful comment

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)
        }
    }
}

All 12 comments

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 Set will call when every request is called.

is there a way to avoid this?

The disadvantage of using middleware is that the Set will call when every request is called.

is there a way to avoid this?

There is a work around

  1. Create a function that passes the *echo.Echo by reference
// 'e' is of type *echo.Echo
// "client" is the item you wish to pass (could be anything)
InjectAddressController(e, client)
  1. Implement that function & set the methods inside there
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 Set will call when every request is called.

whats the cons of calling Set ? performance issueS ?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dre1080 picture dre1080  路  4Comments

toorop picture toorop  路  4Comments

vishr picture vishr  路  3Comments

younisshah picture younisshah  路  4Comments

linux-support picture linux-support  路  3Comments