Mastering Go's ServeMux: Custom Handlers, Redirects, and Closures
This guide explains Go's net/http ServeMux as a request router, demonstrates built‑in handlers like RedirectHandler, shows how to create custom http.Handler implementations, use functions as handlers, and pass variables via closures, complete with runnable code examples.
If you are familiar with the MVC pattern, think of a handler as the controller that executes core logic and generates HTTP response headers and bodies, while a ServeMux (router) acts as a dispatcher mapping URL paths to handlers, typically using a single ServeMux to manage all routes.
The Go net/http package provides a simple yet powerful http.ServeMux and includes helper functions such as http.FileServer() , http.NotFoundHandler() , and http.RedirectHandler() .
Using ServeMux and Built‑in Handlers
Below is a minimal example that creates a ServeMux, registers a redirect handler for /foo, and starts an HTTP server on port 3000.
$ mkdir example
$ cd example
$ go mod init example.com
$ touch main.goFile:
main.go package main
import (
"log"
"net/http"
)
func main() {
// Create an empty ServeMux
mux := http.NewServeMux()
// Create a redirect handler that sends all requests to http://example.org
rh := http.RedirectHandler("http://example.org", 307)
// Register the handler on path /foo
mux.Handle("/foo", rh)
log.Print("FunTester listening...")
// Start the server with the ServeMux
http.ListenAndServe(":3000", mux)
}Running the program and requesting http://localhost:3000/foo returns a 307 Temporary Redirect to http://example.org. Requests to other paths return a 404 Not Found error.
Custom Handlers
Although the net/http package offers built‑in handlers, real‑world applications often need custom handlers. Any type that implements the http.Handler interface can serve as a handler. The interface is defined as:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}A custom handler must implement a ServeHTTP() method. The following example defines a timeHandler struct that returns the current time in a specified format.
type timeHandler struct {
format string
}
func (th timeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
tm := time.Now().Format(th.format)
w.Write([]byte("FunTester current time is: " + tm))
}Registering this handler with a ServeMux:
package main
import (
"log"
"net/http"
"time"
)
type timeHandler struct {
format string
}
func (th timeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
tm := time.Now().Format(th.format)
w.Write([]byte("FunTester current time is: " + tm))
}
func main() {
mux := http.NewServeMux()
th := timeHandler{format: time.RFC1123}
mux.Handle("/time", th)
log.Print("FunTester listening...")
http.ListenAndServe(":3000", mux)
}Accessing http://localhost:3000/time returns a response such as:
$ curl localhost:3000/time
FunTester current time is: Mon, 06 Dec 2021 15:33:21 CETFunctions as Handlers
For simple cases, a function can be used directly as a handler by converting it to http.HandlerFunc. Example:
func timeHandler(w http.ResponseWriter, r *http.Request) {
tm := time.Now().Format(time.RFC1123)
w.Write([]byte("Current time is: " + tm))
}Register with mux.HandleFunc():
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/time", timeHandler)
log.Print("Listening...")
http.ListenAndServe(":3000", mux)
}Passing Variables to Handlers
When more complex data needs to be supplied to a handler, closures can capture variables. The following factory returns an http.Handler that uses a format string supplied at creation time.
func timeHandler(format string) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
tm := time.Now().Format(format)
w.Write([]byte("FunTester current time is: " + tm))
}
return http.HandlerFunc(fn)
}
func main() {
mux := http.NewServeMux()
th := timeHandler(time.RFC1123)
mux.Handle("/time", th)
log.Print("FunTester listening...")
http.ListenAndServe(":3000", mux)
}How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
