You need it. I need it. It's easy to mess up, but just as easy to test. Let's codify it and reuse it so we don't have to re-implement middleware chaining in every project.
The meat and potatoes is the Chain function, which will take an arbitrary
set of middleware and collapse it into a singular middleware that is more
ergonomic to use.
There is a type definition for convienience and readability, aptly named
Middleware, but otherwise there is no stringent requirement as higher
order functions can be used to adapt anything that does not immediately
conform into compatible middleware.
From example/main.go
// Define your REST paths, with any necessary complexity, as usual
mux := http.NewServeMux()
mux.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
responseMap := make(map[string]any)
responseMap["message"] = "Hi Mom!"
responseMap["time"] = time.Now()
if err := json.NewEncoder(w).Encode(responseMap); err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte("500 Internal Server Error"))
}
})
server := http.Server{
Handler: httpmiddlewareutils.Chain(
// Place any other arbitrary middleware here. Any handler that is passed
// as the final handler parameter will be wrapped by the full chain of
// middleware.
panicrecovery.PanicRecoveryMiddleware(),
timingMiddleware(),
slowValidationMiddleware(),
)(mux),
Addr: net.JoinHostPort("0.0.0.0", "8080"),
}This is a common middleware in most rest frameworks, but is something you need
to roll yourself when using the standard library. The out-of-the box implementation
will recover from a panic, respond to the client with 500 Internal Server Error
and record the panic via OTEL.
If OTEL is not initialized this will be a noop without side-effects
Will recover from the immediate panic, but allows you as a developer to specify any arbitrary remediation action desired.