docs(httputil): correct tier from 3 to 2
httputil depends on xerrors (Tier 0) and valid (Tier 1), placing it at Tier 2. No infrastructure or lifecycle dependencies exist in this module.
This commit is contained in:
83
response.go
Normal file
83
response.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package httputil
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"code.nochebuena.dev/go/xerrors"
|
||||
)
|
||||
|
||||
// JSON encodes v as JSON and writes it with the given status code.
|
||||
// Sets Content-Type: application/json.
|
||||
func JSON(w http.ResponseWriter, status int, v any) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(status)
|
||||
_ = json.NewEncoder(w).Encode(v)
|
||||
}
|
||||
|
||||
// NoContent writes a 204 No Content response.
|
||||
func NoContent(w http.ResponseWriter) {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// Error maps err to the appropriate HTTP status code and writes a JSON error body.
|
||||
// Understands *xerrors.Err — extracts Code and Message; fields are included if present.
|
||||
// Falls back to 500 for unknown errors.
|
||||
func Error(w http.ResponseWriter, err error) {
|
||||
if err == nil {
|
||||
JSON(w, http.StatusInternalServerError, errorBody("INTERNAL", "internal server error", nil))
|
||||
return
|
||||
}
|
||||
var xe *xerrors.Err
|
||||
if errors.As(err, &xe) {
|
||||
status := errorCodeToStatus(xe.Code())
|
||||
body := errorBody(string(xe.Code()), xe.Message(), xe.Fields())
|
||||
JSON(w, status, body)
|
||||
return
|
||||
}
|
||||
JSON(w, http.StatusInternalServerError, errorBody("INTERNAL", "internal server error", nil))
|
||||
}
|
||||
|
||||
func errorBody(code, message string, fields map[string]any) map[string]any {
|
||||
m := map[string]any{
|
||||
"code": code,
|
||||
"message": message,
|
||||
}
|
||||
for k, v := range fields {
|
||||
m[k] = v
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// errorCodeToStatus maps a xerrors.Code to an HTTP status code.
|
||||
func errorCodeToStatus(code xerrors.Code) int {
|
||||
switch code {
|
||||
case xerrors.ErrInvalidInput:
|
||||
return http.StatusBadRequest
|
||||
case xerrors.ErrUnauthorized:
|
||||
return http.StatusUnauthorized
|
||||
case xerrors.ErrPermissionDenied:
|
||||
return http.StatusForbidden
|
||||
case xerrors.ErrNotFound:
|
||||
return http.StatusNotFound
|
||||
case xerrors.ErrAlreadyExists:
|
||||
return http.StatusConflict
|
||||
case xerrors.ErrGone:
|
||||
return http.StatusGone
|
||||
case xerrors.ErrPreconditionFailed:
|
||||
return http.StatusPreconditionFailed
|
||||
case xerrors.ErrRateLimited:
|
||||
return http.StatusTooManyRequests
|
||||
case xerrors.ErrInternal:
|
||||
return http.StatusInternalServerError
|
||||
case xerrors.ErrNotImplemented:
|
||||
return http.StatusNotImplemented
|
||||
case xerrors.ErrUnavailable:
|
||||
return http.StatusServiceUnavailable
|
||||
case xerrors.ErrDeadlineExceeded:
|
||||
return http.StatusGatewayTimeout
|
||||
default:
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user