Go by Example: Router Middleware in Gin
Go 1.23
Implement custom middleware in Gin to intercept and process requests. This example demonstrates how to write middleware functions that can modify the request context, log data, or halt execution, providing granular control over the request lifecycle.
Code
package main
import (
"log"
"time"
"github.com/gin-gonic/gin"
)
// Logger is a middleware that logs the request duration
func Logger() gin.HandlerFunc {
return func(c *gin.Context) {
t := time.Now()
// Set a variable in the context
c.Set("example", "12345")
// Process request
c.Next()
// After request
latency := time.Since(t)
log.Print(latency)
// Access the status code we are sending
status := c.Writer.Status()
log.Println(status)
}
}
func main() {
// gin.New() creates a blank engine without default middleware
r := gin.New()
// Register our global middleware
r.Use(Logger())
r.GET("/test", func(c *gin.Context) {
// Retrieve the variable set by the middleware
example := c.MustGet("example").(string)
log.Println("Example variable:", example)
c.JSON(200, gin.H{"status": "ok"})
})
r.Run(":8080")
}Explanation
Middleware in Gin intercepts the HTTP request-response cycle, allowing you to execute code before or after the main route handler. This follows the Chain of Responsibility pattern, where each middleware can either pass control to the next handler or abort the request.
Key concepts:
c.Next(): This function passes control to the next middleware or handler in the chain. Code placed beforec.Next()executes on the request's way in, while code after it executes on the response's way out (ideal for logging latency).- Context Sharing: Use
c.Set()to store data (like user ID or roles) in the context, which can be retrieved by downstream handlers usingc.Get(). - Flow Control: Use
c.Abort()to stop the chain immediately (e.g., if authentication fails), preventing subsequent handlers from running.
Code Breakdown
10
Middleware functions return a gin.HandlerFunc. This standard signature ensures compatibility with Gin's router. The returned function receives the *gin.Context.
14
c.Set stores a key-value pair in the request context. This data persists for the duration of the request and is thread-safe.
17
c.Next() is the pivot point. It pauses the current middleware, executes all pending handlers, and then resumes execution here. Without it, execution would proceed sequentially, but the "after" logic wouldn't wait for the handler to finish.
34
r.Use() registers the middleware globally. Every request handled by this router instance will pass through the Logger middleware.
38
c.MustGet retrieves the value set by the middleware. It panics if the key is missing, so use it only when you are certain the data exists (or use c.Get for a safe check).

