Document all four middleware functions (Recover, RequestID, RequestLogger, CORS), the Logger interface, StatusRecorder, recommended middleware order, and install instructions.
98 lines
2.7 KiB
Markdown
98 lines
2.7 KiB
Markdown
# httpmw
|
|
|
|
`net/http` middleware for transport-layer concerns: panic recovery, CORS, request ID injection, and structured request logging.
|
|
|
|
## Overview
|
|
|
|
Four composable `func(http.Handler) http.Handler` middleware functions:
|
|
|
|
| Middleware | Responsibility |
|
|
|---|---|
|
|
| `Recover()` | Catches panics; writes 500; captures stack trace |
|
|
| `RequestID(generator)` | Generates a unique ID; injects it via `logz.WithRequestID`; sets `X-Request-ID` header |
|
|
| `RequestLogger(logger)` | Logs method, path, status, latency, and request ID after each request |
|
|
| `CORS(origins)` | Sets CORS headers; short-circuits OPTIONS with 204 |
|
|
|
|
No authentication or identity logic lives here — see `httpauth-firebase` for that.
|
|
|
|
## Install
|
|
|
|
```
|
|
go get code.nochebuena.dev/go/httpmw
|
|
```
|
|
|
|
## Usage
|
|
|
|
```go
|
|
// Recommended order — outermost middleware first
|
|
mux.Use(httpmw.Recover())
|
|
mux.Use(httpmw.RequestID(uuid.NewString))
|
|
mux.Use(httpmw.RequestLogger(logger))
|
|
mux.Use(httpmw.CORS([]string{"https://example.com"}))
|
|
```
|
|
|
|
Pass `[]string{"*"}` to `CORS` to allow any origin (development only).
|
|
|
|
## Middleware
|
|
|
|
### Recover
|
|
|
|
```go
|
|
mux.Use(httpmw.Recover())
|
|
```
|
|
|
|
Wraps the handler in a `defer/recover`. On panic, writes `500 Internal Server Error` and captures `debug.Stack()`. No logger is required.
|
|
|
|
### RequestID
|
|
|
|
```go
|
|
mux.Use(httpmw.RequestID(uuid.NewString))
|
|
```
|
|
|
|
Calls `generator()` on every request, stores the ID with `logz.WithRequestID`, and writes it to the `X-Request-ID` response header. Must run before `RequestLogger` so the ID is in context when the logger reads it.
|
|
|
|
### RequestLogger
|
|
|
|
```go
|
|
mux.Use(httpmw.RequestLogger(logger))
|
|
```
|
|
|
|
Logs `method`, `path`, `status`, `latency`, and `request_id` after the inner handler returns. Uses `logger.Error` for 5xx responses and `logger.Info` for all others.
|
|
|
|
### CORS
|
|
|
|
```go
|
|
mux.Use(httpmw.CORS([]string{"https://app.example.com", "https://admin.example.com"}))
|
|
```
|
|
|
|
Sets `Access-Control-Allow-Origin`, `Access-Control-Allow-Methods`, and `Access-Control-Allow-Headers` for matching origins. OPTIONS requests are short-circuited with 204 No Content.
|
|
|
|
Allowed methods: `GET, HEAD, PUT, PATCH, POST, DELETE, OPTIONS`
|
|
Allowed headers: `Content-Type, Authorization, X-Request-ID`
|
|
|
|
## Logger interface
|
|
|
|
`RequestLogger` accepts any value satisfying the `Logger` interface — satisfied by `logz.Logger` via duck typing:
|
|
|
|
```go
|
|
type Logger interface {
|
|
Info(msg string, args ...any)
|
|
Error(msg string, err error, args ...any)
|
|
With(args ...any) Logger
|
|
}
|
|
```
|
|
|
|
## StatusRecorder
|
|
|
|
`StatusRecorder` is exported for use in custom middleware that needs to inspect the written status code:
|
|
|
|
```go
|
|
rec := &httpmw.StatusRecorder{ResponseWriter: w, Status: http.StatusOK}
|
|
next.ServeHTTP(rec, r)
|
|
fmt.Println(rec.Status) // e.g. 404
|
|
```
|
|
|
|
## Dependencies
|
|
|
|
- `code.nochebuena.dev/go/logz`
|