Context

echo.Context represents the context of the current HTTP request. It holds request and response reference, path, path parameters, data, registered handler and APIs to read request and write response. As Context is an interface, it is easy to extend it with custom APIs.

Extending

Define a custom context

  1. type CustomContext struct {
  2. echo.Context
  3. }
  4. func (c *CustomContext) Foo() {
  5. println("foo")
  6. }
  7. func (c *CustomContext) Bar() {
  8. println("bar")
  9. }

Create a middleware to extend default context

  1. e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
  2. return func(c echo.Context) error {
  3. cc := &CustomContext{c}
  4. return next(cc)
  5. }
  6. })

Context - 图1caution

This middleware should be registered before any other middleware.

Context - 图2caution

Custom context cannot be defined in a middleware before the router ran (Pre)

Use in handler

  1. e.GET("/", func(c echo.Context) error {
  2. cc := c.(*CustomContext)
  3. cc.Foo()
  4. cc.Bar()
  5. return cc.String(200, "OK")
  6. })

Concurrency

Context - 图3caution

Context must not be accessed out of the goroutine handling the request. There are two reasons:

  1. Context has functions that are dangerous to execute from multiple goroutines. Therefore, only one goroutine should access it.
  2. Echo uses a pool to create Context‘s. When the request handling finishes, Echo returns the Context to the pool.

See issue 1908 for a “cautionary tale” caused by this reason. Concurrency is complicated. Beware of this pitfall when working with goroutines.

Solution

Use a channel

  1. func(c echo.Context) error {
  2. ca := make(chan string, 1) // To prevent this channel from blocking, size is set to 1.
  3. r := c.Request()
  4. method := r.Method
  5. go func() {
  6. // This function must not touch the Context.
  7. fmt.Printf("Method: %s\n", method)
  8. // Do some long running operations...
  9. ca <- "Hey!"
  10. }()
  11. select {
  12. case result := <-ca:
  13. return c.String(http.StatusOK, "Result: "+result)
  14. case <-c.Request().Context().Done(): // Check context.
  15. // If it reaches here, this means that context was canceled (a timeout was reached, etc.).
  16. return nil
  17. }
  18. }