Echo: question: how to pass a pre-allocated Context to Echo.ServeHTTP?

Created on 14 Jun 2016  路  10Comments  路  Source: labstack/echo

I am build a multi-subdomain HTTP service following the example using an host map
I would like to pass my datastore object into the context but the ServeHTTP func uses a newly-allocated one so it never goes down to my handler

Is there any work-around?
Should I use a global datastore object?

Thanks

// Server
    e := echo.New()
    e.Any("/*", func(c echo.Context) (err error) {
        req := c.Request()
        res := c.Response()
        host := hosts[req.Host()]

        if host == nil {
            err = echo.ErrNotFound
        } else {
            c.Set("DataStore", persistence.DataStore) <-- HERE IS MY ISSUE
            host.Echo.ServeHTTP(req, res)
        }

        return err
    })
    e.Run(standard.New(":8080"))
question

Most helpful comment

func dataSourceMiddleware(dataStore DB)  echo.MiddlewareFunc {
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) err error {
            c.Set("db", dataStore)
            return next(c)
        }
    }
}

blog.GET("/", func(c echo.Context) error {
    db, ok := c.Get("db").(DB)
    return c.String(http.StatusOK, "Blog")
}

All 10 comments

What about middleware? You can put db object into context in middleware.

sorry but I don't understand how your answer applies to my situation

I am starting from this example: https://echo.labstack.com/recipes/subdomains

thanks

You can put db object into context on subdomain's echo instance declaration

Thanks for your help, but I still don't understand how to accomplish this...

Which function should I call on the Echo object?

Also, please check first three lines of ServeHTTP function:

func (e *Echo) ServeHTTP(req engine.Request, res engine.Response) {
    c := e.pool.Get().(*echoContext)
    c.Reset(req, res)

a new Context is obtained from the pool and then reset

package main

import (
    "net/http"

    "github.com/labstack/echo"
    "github.com/labstack/echo/engine/standard"
    "github.com/labstack/echo/middleware"
)

type (
    Host struct {
        Echo *echo.Echo
    }
)

func main() {
    // Hosts
    hosts := make(map[string]*Host)

    //-----
    // API
    //-----

    api := echo.New()
    api.Use(middleware.Logger())
    api.Use(middleware.Recover())
    api.Use(dataSourceMiddleware(persistence.DataStore)) // Put your dbinstance to context

    hosts["api.localhost:1323"] = &Host{api}

    api.GET("/", func(c echo.Context) error {
        return c.String(http.StatusOK, "API")
    })

    //------
    // Blog
    //------

    blog := echo.New()
    blog.Use(middleware.Logger())
    blog.Use(middleware.Recover())
    blog.Use(dataSourceMiddleware(persistence.DataStore)) // Put your dbinstance to context

    hosts["blog.localhost:1323"] = &Host{blog}

    blog.GET("/", func(c echo.Context) error {
        return c.String(http.StatusOK, "Blog")
    })

    // Server
    e := echo.New()
    e.Any("/*", func(c echo.Context) (err error) {
        req := c.Request()
        res := c.Response()
        host := hosts[req.Host()]

        if host == nil {
            err = echo.ErrNotFound
        } else {
            host.Echo.ServeHTTP(req, res)
        }

        return
    })
    e.Run(standard.New(":1323"))
}

thank you very much for your help.
I understand that the data store is now available in the custom middleware.

I still don't understand how I can retrieve my db instance in the handler

for example

blog.GET("/",` func(c echo.Context) error {
ds := ** GET DB INSTANCE HERE **
return c.String(http.StatusOK, "Blog")
})

For example I want to reply with a JSON that lists all the rows from a table

func dataSourceMiddleware(dataStore DB)  echo.MiddlewareFunc {
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) err error {
            c.Set("db", dataStore)
            return next(c)
        }
    }
}

blog.GET("/", func(c echo.Context) error {
    db, ok := c.Get("db").(DB)
    return c.String(http.StatusOK, "Blog")
}

I am going to try it and let you know.
BTW is it a recommended approach or should I leave the DB instance as a global and live my life in peace?

@genez Context is meant for request scope variables only. You should have a struct holding DB instance and use it. Something like below:

type Handler struct {
  DB *Mongo
}

func (h *Handler) func Save(c echo.Context) error {
// Save using h.DB
}
e := echo.New()
h := Handler{DB: mongo}

// Register your routes
e.POST("/save", h.Save)

It surely would be nice having an example for something like this
https://joeshaw.org/revisiting-context-and-http-handler-for-go-17/

Was this page helpful?
0 / 5 - 0 ratings

Related issues

vishr picture vishr  路  3Comments

arun0009 picture arun0009  路  3Comments

runner-mei picture runner-mei  路  4Comments

alexzorin picture alexzorin  路  3Comments

syntaqx picture syntaqx  路  3Comments