Files
web/httputil/handle.go
T

74 lines
2.4 KiB
Go
Raw Normal View History

package httputil
import (
"context"
"encoding/json"
"net/http"
"code.nochebuena.dev/einherjar/contracts/logging"
"code.nochebuena.dev/einherjar/core/valid"
"code.nochebuena.dev/einherjar/core/xerrors"
)
// Handle adapts a typed business function to http.HandlerFunc.
// - Decodes the JSON request body into Req.
// - Validates Req using the provided [valid.Validator].
// - Calls fn with the request context and decoded Req.
// - Encodes Res as JSON with HTTP 200 on success.
// - On error: logs via [Error] (level derived from HTTP status) and writes the standardized JSON body.
func Handle[Req, Res any](v valid.Validator, logger logging.Logger, fn func(ctx context.Context, req Req) (Res, error)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req Req
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
Error(logger, w, r, xerrors.New(xerrors.ErrInvalidInput, "invalid JSON: "+err.Error()))
return
}
if err := v.Struct(req); err != nil {
Error(logger, w, r, err)
return
}
res, err := fn(r.Context(), req)
if err != nil {
Error(logger, w, r, err)
return
}
JSON(w, http.StatusOK, res)
}
}
// HandleNoBody adapts a typed function with no request body (GET, HEAD).
// Calls fn with the request context; encodes the result as JSON with HTTP 200.
// On error: logs via [Error] and writes the standardized JSON body.
func HandleNoBody[Res any](logger logging.Logger, fn func(ctx context.Context) (Res, error)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
res, err := fn(r.Context())
if err != nil {
Error(logger, w, r, err)
return
}
JSON(w, http.StatusOK, res)
}
}
// HandleEmpty adapts a typed function with a request body but no response body.
// Decodes and validates Req, calls fn, returns 204 No Content on success.
// On error: logs via [Error] and writes the standardized JSON body.
func HandleEmpty[Req any](v valid.Validator, logger logging.Logger, fn func(ctx context.Context, req Req) error) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req Req
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
Error(logger, w, r, xerrors.New(xerrors.ErrInvalidInput, "invalid JSON: "+err.Error()))
return
}
if err := v.Struct(req); err != nil {
Error(logger, w, r, err)
return
}
if err := fn(r.Context(), req); err != nil {
Error(logger, w, r, err)
return
}
NoContent(w)
}
}