Middleware is a crucial part of building robust and scalable web applications. It provides a mechanism to modify incoming requests and outgoing responses, allowing you to implement features such as logging, authentication, error handling, and more. In this blog post, we'll explore how to write middleware in Go using the popular net/http package.
What is Middleware?
Middleware is essentially a function that wraps an HTTP handler. It can process requests before they reach the final handler and modify responses before they are sent back to the client. This is particularly useful for cross-cutting concerns like:
- Logging request details
- Authentication and authorization
- Input validation
- Response compression
- Error handling
Creating Middleware in Go
Let's start by setting up a simple HTTP server in Go.
Basic HTTP Server
First, create a new Go project and initialize a basic HTTP server.
package main
import (
"fmt"
"net/http"
)
func helloHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
}
func main() {
http.HandleFunc("/", helloHandler)
http.ListenAndServe(":8080", nil)
}
Running this code will start a web server on http://localhost:8080
that responds with "Hello, World!" to any request.
Writing Your First Middleware
Middleware in Go is typically a function that takes an http.Handler
and returns another http.Handler
. Hereâs a simple logging middleware that logs each incoming request.
package main
import (
"log"
"net/http"
"time"
)
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
log.Printf("Started %s %s", r.Method, r.URL.Path)
next.ServeHTTP(w, r)
log.Printf("Completed %s in %v", r.URL.Path, time.Since(start))
})
}
func main() {
helloHandler := http.HandlerFunc(helloHandler)
http.Handle("/", loggingMiddleware(helloHandler))
http.ListenAndServe(":8080", nil)
}
In this middleware, we log the HTTP method and URL path at the start of the request and log the time taken to process the request after the handler has executed.
Chaining Multiple Middleware
You can chain multiple middleware functions to handle different concerns. Hereâs how you can do it:
func chainMiddleware(h http.Handler, middlewares ...func(http.Handler) http.Handler) http.Handler {
for _, middleware := range middlewares {
h = middleware(h)
}
return h
}
func main() {
helloHandler := http.HandlerFunc(helloHandler)
middlewares := []func(http.Handler) http.Handler{
loggingMiddleware,
// Add more middleware here
}
http.Handle("/", chainMiddleware(helloHandler, middlewares...))
http.ListenAndServe(":8080", nil)
}
Advanced Middleware Patterns
For more complex scenarios, you might want to handle response modification or wrap multiple handlers. Hereâs an example of a middleware that compresses the response using gzip:
import (
"compress/gzip"
"io"
"net/http"
"strings"
)
func gzipMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
next.ServeHTTP(w, r)
return
}
gz := gzip.NewWriter(w)
defer gz.Close()
w.Header().Set("Content-Encoding", "gzip")
gzw := gzipResponseWriter{Writer: gz, ResponseWriter: w}
next.ServeHTTP(gzw, r)
})
}
type gzipResponseWriter struct {
io.Writer
http.ResponseWriter
}
func (w gzipResponseWriter) Write(b []byte) (int, error) {
return w.Writer.Write(b)
}
func main() {
helloHandler := http.HandlerFunc(helloHandler)
middlewares := []func(http.Handler) http.Handler{
loggingMiddleware,
authMiddleware,
gzipMiddleware,
}
http.Handle("/", chainMiddleware(helloHandler, middlewares...))
http.ListenAndServe(":8080", nil)
}
Conclusion
Middleware is a powerful concept that allows you to build modular and reusable components in your Go web applications. By understanding how to write and chain middleware, you can handle various cross-cutting concerns effectively. Whether you're logging requests, handling authentication, or compressing responses, middleware provides a clean and elegant way to manage these tasks.
291 views