Skip to main content

Context

The slim.Context interface is the context for the Slim handler function for the current HTTP request. It includes references to the request and response, route matching information, runtime data, etc., and is used for reading requests and writing responses.

tip

We need to distinguish between slim.Context and HTTP request context (http.Request.Context).

Extending Context

Because slim.Context is an interface, we can easily extend it.

Define our own context:

Extending context
type CustomContext struct {
slim.Context
}

func (c *CustomContext) Foo() {
println("foo")
}

func (c *CustomContext) Bar() {
println("bar")
}

Then create a middleware to extend the default context and pass this new context downstream:

Using custom context
s.Use(func(c slim.Context, next slim.HandlerFunc) error {
cc := &CustomContext{c}
return next(cc)
})
caution

This middleware should be registered before other middlewares.

Then we can use it in handler functions:

s.GET("/", func(c slim.Context) error {
cc := c.(*CustomContext)
cc.Foo()
cc.Bar()
return cc.String(200, "OK")
})

Concurrent Operations

caution

slim.Context cannot be accessed outside of the goroutine handling the request for two reasons:

  1. The default implementation of slim.Context is not concurrency-safe, so we should only access it in one goroutine.
  2. Additionally, slim.Context is created using the object pool sync.Pool, and will be returned to the object pool after request processing is complete for future reuse.

Due to the complexity of concurrency, be very careful to note this pitfall when using goroutines.

We can use channels to block request processing, waiting for handler function's child goroutines to finish before completing request processing:

Concurrency example
func(c slim.Context) error {
// First, we prepare a non-blocking channel with capacity 1
ca := make(chan string, 1)
r := c.Request()
method := r.Method

go func() {
// We should prepare context-related data outside this function beforehand,
// and never use the slim.Context instance `c` to get data here.
fmt.Printf("Method: %s\n", method)

// Here, we can do some time-consuming operations
// Then use the channel to tell the handler processing is complete.
ca <- "Hey!"
}()

select {
case result := <-ca:
// Receive the result processed by other goroutines
return c.String(http.StatusOK, "Result: "+result)
case <-c.Done():
// If the code runs here, it means the context was cancelled (e.g., timeout, etc.).
return nil
}
}