feat(web): httputil.WithStatus — configurable success status on the handler adapters (v1.5.0)

This commit is contained in:
2026-08-12 17:55:28 -06:00
parent 80b28dfcc4
commit bcccfef443
8 changed files with 217 additions and 23 deletions
+40
View File
@@ -0,0 +1,40 @@
package httputil
import "fmt"
// Option configures a Handle* adapter. With no options each adapter writes its
// default success status (200 for the body-returning adapters, 204 for
// [HandleEmpty]). Options are applied once at wiring time, not per request.
type Option func(*options)
type options struct {
status int
}
// WithStatus overrides the success status an adapter writes — e.g.
// WithStatus(http.StatusCreated) for a POST that creates a resource, or
// WithStatus(http.StatusAccepted) for an async [HandleEmpty].
//
// It exists because the Handle* adapters own only the happy path: they always
// write a success response, so the status is theirs to set, while error statuses
// are derived separately from the returned xerror by [Error]. The code must
// therefore be 2xx — anything else is a routing mistake, since an error status
// never belongs on the success path. WithStatus panics on a non-2xx code, and
// because routes are wired at startup that panic surfaces at boot: the service
// fails to start rather than emitting a wrong status at request time. (mw.Recover
// guards requests, so it does not catch a wiring-time panic — which is the point.)
func WithStatus(code int) Option {
if code < 200 || code > 299 {
panic(fmt.Sprintf("httputil.WithStatus: success status must be 2xx, got %d", code))
}
return func(o *options) { o.status = code }
}
// resolveStatus folds opts over the adapter's default success status.
func resolveStatus(def int, opts []Option) int {
o := options{status: def}
for _, opt := range opts {
opt(&o)
}
return o.status
}