Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
32831d5a06
|
|||
|
2298846280
|
@@ -11,7 +11,7 @@ authentication or identity logic lives here — see `httpauth-firebase` for that
|
|||||||
|
|
||||||
## Tier & Dependencies
|
## Tier & Dependencies
|
||||||
|
|
||||||
**Tier:** 3 (transport layer; depends on Tier 0 `logz`)
|
**Tier:** 2 (transport layer; depends on Tier 1 `logz`)
|
||||||
**Module:** `code.nochebuena.dev/go/httpmw`
|
**Module:** `code.nochebuena.dev/go/httpmw`
|
||||||
**Direct imports:** `code.nochebuena.dev/go/logz`
|
**Direct imports:** `code.nochebuena.dev/go/logz`
|
||||||
|
|
||||||
|
|||||||
97
README.md
Normal file
97
README.md
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
# 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`
|
||||||
Reference in New Issue
Block a user