Echo Request Context
Introduction
In the Echo framework, the Context (often abbreviated as c
in code examples) is a crucial concept that represents the context of an HTTP request. It holds information about the current HTTP request and response, and provides methods to work with them efficiently.
Understanding how to use the Echo Context is fundamental to building web applications with the Echo framework, as nearly all your handler functions will receive a Context instance as their parameter.
What is Echo Context?
Echo's Context is an interface that wraps an HTTP request and response. It provides many helpful methods to:
- Access request data (parameters, headers, body, etc.)
- Send responses (JSON, HTML, files, etc.)
- Store and retrieve values specific to this request
- Handle redirects and errors
- Validate input data
The Context is passed through the request lifecycle, allowing you to access and modify it at different stages of processing.
Basic Usage of Echo Context
Here's a simple example of how Context is used in an Echo handler function:
package main
import (
"github.com/labstack/echo/v4"
"net/http"
)
func main() {
e := echo.New()
e.GET("/hello", func(c echo.Context) error {
// c is the Echo Context
return c.String(http.StatusOK, "Hello, World!")
})
e.Start(":8080")
}
When a request is made to the /hello
endpoint, Echo creates a new Context object and passes it to your handler function.
Accessing Request Information
One of the primary uses of Context is to access information about the incoming request.
Path Parameters
Path parameters are parts of the URL path that can change:
e.GET("/users/:id", func(c echo.Context) error {
// Extract the 'id' parameter from the URL
id := c.Param("id")
return c.String(http.StatusOK, "User ID: " + id)
})
Example:
- Request:
GET /users/123
- Output:
User ID: 123
Query Parameters
Query parameters are the key-value pairs in the URL after the ?
symbol:
e.GET("/search", func(c echo.Context) error {
// Get query parameter
query := c.QueryParam("q")
return c.String(http.StatusOK, "Searching for: " + query)
})
Example:
- Request:
GET /search?q=echo+framework
- Output:
Searching for: echo framework
Form Data
To access form data submitted via POST requests:
e.POST("/submit", func(c echo.Context) error {
name := c.FormValue("name")
email := c.FormValue("email")
return c.JSON(http.StatusOK, map[string]string{
"name": name,
"email": email,
})
})
Request Headers
To read request headers:
e.GET("/headers", func(c echo.Context) error {
userAgent := c.Request().Header.Get("User-Agent")
return c.String(http.StatusOK, "Your User-Agent: " + userAgent)
})
Sending Responses
Context provides various methods to send different types of responses:
Sending Text Response
e.GET("/text", func(c echo.Context) error {
return c.String(http.StatusOK, "This is a plain text response")
})
Sending JSON Response
e.GET("/json", func(c echo.Context) error {
data := map[string]interface{}{
"name": "John Doe",
"age": 30,
"active": true,
}
return c.JSON(http.StatusOK, data)
})
Output:
{
"name": "John Doe",
"age": 30,
"active": true
}
Sending HTML Response
e.GET("/html", func(c echo.Context) error {
return c.HTML(http.StatusOK, "<h1>Hello, World!</h1>")
})
Storing and Retrieving Values in Context
Context allows you to store and retrieve values during the request lifecycle. This is particularly useful for passing data between middleware and handlers.
// Middleware that sets a value in context
func setContextValue(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// Set a value in the context
c.Set("user", "john_doe")
// Continue to the next middleware or handler
return next(c)
}
}
func main() {
e := echo.New()
// Apply the middleware
e.Use(setContextValue)
e.GET("/profile", func(c echo.Context) error {
// Retrieve the value from context
username := c.Get("user").(string)
return c.String(http.StatusOK, "Profile for: " + username)
})
e.Start(":8080")
}
Example:
- Request:
GET /profile
- Output:
Profile for: john_doe
Real-World Application: Authentication Middleware
A common real-world use of Context is in authentication middleware, where you validate a user's credentials and store the authenticated user in the context for later use:
// Authentication middleware
func authMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// Get token from header
token := c.Request().Header.Get("Authorization")
// In a real app, you would validate the token
if token == "valid-token" {
// Store user info in context for later use
c.Set("authenticated", true)
c.Set("userId", "user123")
return next(c)
}
// If token is invalid, return 401 Unauthorized
return c.JSON(http.StatusUnauthorized, map[string]string{
"error": "Authentication failed",
})
}
}
func main() {
e := echo.New()
// Public routes
e.GET("/public", func(c echo.Context) error {
return c.String(http.StatusOK, "This is a public endpoint")
})
// Private routes with authentication
privateGroup := e.Group("/private")
privateGroup.Use(authMiddleware)
privateGroup.GET("/profile", func(c echo.Context) error {
// Access user information from context
userId := c.Get("userId").(string)
return c.JSON(http.StatusOK, map[string]interface{}{
"message": "You're authenticated",
"userId": userId,
})
})
e.Start(":8080")
}
Practical Example: A Simple API with Context
Let's build a more comprehensive example of a shopping cart API that demonstrates various Context features:
package main
import (
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"net/http"
)
type Item struct {
ID string `json:"id"`
Name string `json:"name"`
Price float64 `json:"price"`
}
type CartItem struct {
Item Item `json:"item"`
Quantity int `json:"quantity"`
}
// In-memory database for demonstration
var items = map[string]Item{
"1": {ID: "1", Name: "Laptop", Price: 1299.99},
"2": {ID: "2", Name: "Phone", Price: 799.99},
"3": {ID: "3", Name: "Headphones", Price: 199.99},
}
func main() {
e := echo.New()
// Middleware
e.Use(middleware.Logger())
e.Use(middleware.Recover())
// Shopping cart middleware - creates an empty cart for each request
e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
c.Set("cart", make([]CartItem, 0))
return next(c)
}
})
// Routes
e.GET("/items", getItems)
e.GET("/items/:id", getItem)
e.POST("/cart/add", addToCart)
e.GET("/cart", viewCart)
e.Start(":8080")
}
// Get all available items
func getItems(c echo.Context) error {
return c.JSON(http.StatusOK, items)
}
// Get a specific item by ID
func getItem(c echo.Context) error {
id := c.Param("id")
item, exists := items[id]
if !exists {
return c.JSON(http.StatusNotFound, map[string]string{
"error": "Item not found",
})
}
return c.JSON(http.StatusOK, item)
}
// Add item to cart
func addToCart(c echo.Context) error {
// Parse request
type AddRequest struct {
ItemID string `json:"item_id"`
Quantity int `json:"quantity"`
}
req := new(AddRequest)
if err := c.Bind(req); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{
"error": "Invalid request format",
})
}
// Validate item exists
item, exists := items[req.ItemID]
if !exists {
return c.JSON(http.StatusNotFound, map[string]string{
"error": "Item not found",
})
}
// Get cart from context
cart, ok := c.Get("cart").([]CartItem)
if !ok {
return c.JSON(http.StatusInternalServerError, map[string]string{
"error": "Cart not initialized",
})
}
// Add item to cart
cartItem := CartItem{
Item: item,
Quantity: req.Quantity,
}
cart = append(cart, cartItem)
// Update cart in context
c.Set("cart", cart)
return c.JSON(http.StatusOK, map[string]interface{}{
"message": "Item added to cart",
"cart": cart,
})
}
// View current cart
func viewCart(c echo.Context) error {
cart, ok := c.Get("cart").([]CartItem)
if !ok {
return c.JSON(http.StatusInternalServerError, map[string]string{
"error": "Cart not initialized",
})
}
// Calculate total
total := 0.0
for _, item := range cart {
total += item.Item.Price * float64(item.Quantity)
}
return c.JSON(http.StatusOK, map[string]interface{}{
"items": cart,
"total": total,
})
}
This example shows how you can use Context to:
- Store session data (the shopping cart)
- Parse request parameters and JSON bodies
- Return different types of responses based on request handling results
Summary
The Echo Context is a powerful abstraction that provides a clean interface to work with HTTP requests and responses. Understanding how to use Context effectively is key to building robust web applications with Echo.
Key points to remember:
- Context provides access to HTTP request data (params, query, form, etc.)
- It offers methods to send various response types (String, JSON, HTML, etc.)
- You can use Context to store and retrieve values during request handling
- Context is passed through the middleware chain and to handler functions
By mastering the Echo Context, you'll be able to build more sophisticated web applications and APIs with the Echo framework.
Additional Resources
Exercises
-
Basic Context Practice: Create a simple Echo app with endpoints that use different Context methods to return responses (String, JSON, HTML).
-
Path Parameters: Build an API endpoint that accepts multiple path parameters and uses them in the response.
-
Middleware Context: Create a middleware that adds a timestamp to the Context and a handler that displays how long ago the request started.
-
Form Handling: Build a simple form submission handler that validates input and returns appropriate responses.
-
Advanced Cart API: Extend the shopping cart example to support removing items and updating quantities.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)