httperrors

package
v3.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 13, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Index

Constants

View Source
const (
	ReasonOK                 = "OK"
	ReasonCanceled           = "CANCELED"
	ReasonUnknown            = "UNKNOWN"
	ReasonInvalidArgument    = "INVALID_ARGUMENT"
	ReasonDeadlineExceeded   = "DEADLINE_EXCEEDED"
	ReasonNotFound           = "NOT_FOUND"
	ReasonAlreadyExists      = "ALREADY_EXISTS"
	ReasonPermissionDenied   = "PERMISSION_DENIED"
	ReasonResourceExhausted  = "RESOURCE_EXHAUSTED"
	ReasonFailedPrecondition = "FAILED_PRECONDITION"
	ReasonAborted            = "ABORTED"
	ReasonOutOfRange         = "OUT_OF_RANGE"
	ReasonUnimplemented      = "UNIMPLEMENTED"
	ReasonInternal           = "INTERNAL"
	ReasonUnavailable        = "UNAVAILABLE"
	ReasonDataLoss           = "DATA_LOSS"
	ReasonUnauthenticated    = "UNAUTHENTICATED"
)

Common reason constants for HTTP errors. These replace the protobuf ErrorReason enum from statusx.

Variables

This section is empty.

Functions

func AssertFieldViolations

func AssertFieldViolations(t *testing.T, err error, fvs ...*FieldViolation)

func Error

func Error(httpStatus int, reason, message string) error

func ErrorMiddleware

func ErrorMiddleware(conf *HTTPErrorMiddlewareConfig) func(http.Handler) http.Handler

ErrorMiddleware creates an HTTP middleware that: 1. Injects i18n context into the request 2. Recovers from panics containing errors 3. Translates and writes structured JSON error responses

Currently uses panic-based error propagation to keep handler signatures as standard http.HandlerFunc. NOTE: In the future, this may be changed to support a handler signature that returns error directly (e.g., type HandlerFunc func(w http.ResponseWriter, r *http.Request) error). The panic-based approach is chosen for now to maintain compatibility with standard http.HandlerFunc.

func Errorf

func Errorf(httpStatus int, reason, format string, a ...any) error

func FormatField

func FormatField(field string, formatFunc func(string) string) string

FormatField formats a dotted field path by applying a formatting function to each segment while preserving array index notations (e.g., [0], [1]).

Parameters:

  • field: The original field path (e.g., "user_info.addresses[0].street_name")
  • formatFunc: Function to apply to each field segment (e.g., lo.CamelCase)

Returns:

  • The formatted field path with proper array index preservation

Example:

FormatField("user_info.addresses[0].street_name", lo.CamelCase)
Returns: "userInfo.addresses[0].streetName"

func HandleError

func HandleError(conf *HTTPErrorMiddlewareConfig, w http.ResponseWriter, r *http.Request, err error)

HandleError translates and writes a structured JSON error response for the given error. It is kept for backward compatibility and intentionally does not return write errors.

func NewErrorMiddleware

func NewErrorMiddleware(ib *i18nx.I18N) func(http.Handler) http.Handler

NewErrorMiddleware is a convenience function that creates an ErrorMiddleware with default configuration.

func Reason

func Reason(err error) string

func ReasonFromStatus

func ReasonFromStatus(httpStatus int) string

ReasonFromStatus returns a default reason string for a given HTTP status code.

func StatusCode

func StatusCode(err error) int

func TranslateError

func TranslateError(err error, ib *i18nx.I18N, lang language.Tag) error

TranslateError translates error messages and field violations using the provided i18n instance and language. It always returns an error derived from a status representation of the input error (typically a *StatusError);

Translation behavior:

  • The original message is preserved in the `message` field.
  • The translated text is stored in the `localizedMessage` field.
  • If the Localized template is set, its key and args are used for translation.
  • Otherwise, the error reason is used as the i18n key fallback.
  • If already translated (localized is nil), translation is skipped.

func TranslateStatusErrorOnly

func TranslateStatusErrorOnly(err error, ib *i18nx.I18N, lang language.Tag) (error, bool)

TranslateStatusErrorOnly translates only StatusError types, returning the error and a boolean indicating success

func Validate

func Validate(ctx context.Context, input any) error

func WrapHandlerFunc

func WrapHandlerFunc(conf *HTTPErrorMiddlewareConfig, handler http.HandlerFunc) http.HandlerFunc

WrapHandlerFunc wraps a single http.HandlerFunc with httperrors panic recovery and i18n translation. This is useful when only some handlers in a mux use httperrors, and a global middleware is not appropriate.

Usage:

mux.HandleFunc("/api/users/{id}", httperrors.WrapHandlerFunc(conf, h.GetUser))
mux.HandleFunc("/legacy/other", legacyHandler) // not wrapped

func WriteError

func WriteError(conf *HTTPErrorMiddlewareConfig, w http.ResponseWriter, r *http.Request, err error) error

WriteError translates and writes a structured JSON error response for the given error. This is intended for use inside individual handlers that want to explicitly handle errors without relying on panic-based middleware.

Usage:

func (h *UserHandler) GetUser(w http.ResponseWriter, r *http.Request) {
    user, err := h.userService.GetUser(r.Context(), r.PathValue("id"))
    if err != nil {
        if herr := httperrors.WriteError(h.conf, w, r, err); herr != nil {
            slog.ErrorContext(r.Context(), "Failed to write http response error", "error", err, "writeError", herr)
        }
        return
    }
    json.NewEncoder(w).Encode(user)
}

func WriteJSONError

func WriteJSONError(err error, w http.ResponseWriter) error

WriteJSONError writes a structured JSON error response from an error. The HTTP status code is set from the Status object. The response body follows the ErrorResponse format with camelCase fields.

Types

type ContextValidator

type ContextValidator interface {
	Validate(ctx context.Context) error
}

type ErrorResponse

type ErrorResponse struct {
	Code             string                         `json:"code"`
	Message          string                         `json:"message"`
	LocalizedMessage string                         `json:"localizedMessage,omitempty"`
	Metadata         map[string]string              `json:"metadata,omitempty"`
	FieldViolations  []*ErrorResponseFieldViolation `json:"fieldViolations,omitempty"`
}

ErrorResponse is the standard JSON error response body. Fields use camelCase for frontend compatibility.

type ErrorResponseFieldViolation

type ErrorResponseFieldViolation struct {
	Field            string `json:"field"`
	Code             string `json:"code"`
	Message          string `json:"message"`
	LocalizedMessage string `json:"localizedMessage,omitempty"`
}

ErrorResponseFieldViolation represents a single field violation in the JSON error response.

type FieldViolation

type FieldViolation struct {
	// contains filtered or unexported fields
}

FieldViolation represents a field-level validation violation with localization capability.

Translation preserves the original description and stores the translated text in localizedMessage. Priority order for localized messages:

  1. LocalizedMessage (highest priority - pre-translated, ready to use)
  2. Localized (lower priority - template that needs translation via middleware)

func NewFieldViolation

func NewFieldViolation(field, reason, description string) *FieldViolation

NewFieldViolation creates a new field validation violation. The reason serves as the error identifier and will be used as the i18n key fallback during translation.

func NewFieldViolationf

func NewFieldViolationf(field, reason, format string, args ...any) *FieldViolation

NewFieldViolationf creates a new field validation violation with a formatted description.

func (*FieldViolation) Clone

func (f *FieldViolation) Clone() *FieldViolation

Clone creates a deep copy of this FieldViolation.

func (*FieldViolation) Description

func (f *FieldViolation) Description() string

Description returns the human-readable description of the violation.

func (*FieldViolation) Field

func (f *FieldViolation) Field() string

Field returns the field name that caused the violation.

func (*FieldViolation) GetLocalized

func (f *FieldViolation) GetLocalized() *Localized

GetLocalized returns the localization template if set. Returns nil if no localization template is available.

func (*FieldViolation) GetLocalizedMessage

func (f *FieldViolation) GetLocalizedMessage() *LocalizedMessage

GetLocalizedMessage returns the pre-translated message if available. Returns nil if no pre-translated message is set.

func (*FieldViolation) Reason

func (f *FieldViolation) Reason() string

Reason returns the error reason code.

func (*FieldViolation) WithLocalized

func (f *FieldViolation) WithLocalized(key string, args ...any) *FieldViolation

WithLocalized sets a custom i18n key and template arguments. This sets a specific i18n key instead of relying on the reason as fallback during translation.

func (*FieldViolation) WithLocalizedArgs

func (f *FieldViolation) WithLocalizedArgs(args ...any) *FieldViolation

WithLocalizedArgs sets template arguments for i18n. This method relies on the constructor invariant that localized is initialized with the reason as the default key. Use WithLocalized if you need to change the translation key before setting args.

type FieldViolations

type FieldViolations []*FieldViolation

func FlattenFieldViolations

func FlattenFieldViolations(inputs ...any) (FieldViolations, error)

FlattenFieldViolations flattens various field violation types into a unified FieldViolations slice. Supports *FieldViolation, []*FieldViolation, FieldViolations. Mixed types are allowed in a single call.

Note: For error and *Status inputs, use ToFieldViolations(err, field) or status.ToFieldViolations(field) first to specify the field name, then pass the result to this function.

func PrependField

func PrependField(field string, fvs ...*FieldViolation) FieldViolations

PrependField prepends a field name to the field name of each field violation.

func ToFieldViolations

func ToFieldViolations(err error, field string) FieldViolations

ToFieldViolations converts any error to field violations for the specified field. Simple behavior:

  • If field is empty: returns only nested field violations without prefix
  • If field is non-empty: returns only nested field violations with the specified field prefix

This design extracts meaningful field-level violations from container errors.

func (FieldViolations) PrependField

func (fvs FieldViolations) PrependField(field string) FieldViolations

type HTTPErrorMiddlewareConfig

type HTTPErrorMiddlewareConfig struct {
	I18N *i18nx.I18N
	// contains filtered or unexported fields
}

func (*HTTPErrorMiddlewareConfig) WithHTTPWriteErrorHook

func (c *HTTPErrorMiddlewareConfig) WithHTTPWriteErrorHook(hooks ...hook.Hook[HTTPWriteErrorFunc]) *HTTPErrorMiddlewareConfig

type HTTPWriteErrorFunc

type HTTPWriteErrorFunc func(ctx context.Context, input *HTTPWriteErrorInput) (*HTTPWriteErrorOutput, error)

type HTTPWriteErrorInput

type HTTPWriteErrorInput struct {
	Conf *HTTPErrorMiddlewareConfig
	W    http.ResponseWriter
	R    *http.Request
	Err  error
}

type HTTPWriteErrorOutput

type HTTPWriteErrorOutput struct {
	Written bool
}

type Localized

type Localized struct {
	// contains filtered or unexported fields
}

Localized represents a localization template with a key and optional arguments.

func (*Localized) Args

func (l *Localized) Args() []any

func (*Localized) Clone

func (l *Localized) Clone() *Localized

func (*Localized) Key

func (l *Localized) Key() string

type LocalizedMessage

type LocalizedMessage struct {
	Locale  string
	Message string
}

LocalizedMessage represents a pre-translated message with its locale.

func (*LocalizedMessage) Clone

func (lm *LocalizedMessage) Clone() *LocalizedMessage

type Status

type Status struct {
	// contains filtered or unexported fields
}

func BadRequest

func BadRequest(inputs ...any) *Status

BadRequest creates a new Status with http.StatusBadRequest (400) and a flattened list of field violations.

func Clone

func Clone(s *Status) *Status

func Convert

func Convert(err error) *Status

func FromError

func FromError(err error) (s *Status, ok bool)

func New

func New(httpStatus int, reason, message string) *Status

New creates a Status with the specified HTTP status code, reason, and message.

For non-2xx status codes, it automatically captures a stacktrace at creation time, which provides valuable debugging context without manual instrumentation.

Parameters:

  • httpStatus: The HTTP status code (e.g., 400, 404, 500)
  • reason: A string identifier used as the error reason and i18n key fallback during translation
  • message: A human-readable message for debugging purposes

The reason serves as both the error identifier and the i18n key. The reason is immediately fixed as the i18n key at creation time. Use WithLocalized() to override with a specific i18n key and args if needed. Use WithLocalizedArgs() to add template arguments while preserving the current key. Returns a Status object that can be further enriched with metadata or localization.

func NewStatus

func NewStatus(httpStatus int, message string) *Status

NewStatus creates a Status with automatically derived reason from the HTTP status code. This is a convenience function that uses ReasonFromStatus to generate the reason.

func NewStatusf

func NewStatusf(httpStatus int, format string, a ...any) *Status

NewStatusf creates a Status with automatically derived reason and formatted message.

func Newf

func Newf(httpStatus int, reason, format string, a ...any) *Status

func Wrap

func Wrap(err error, httpStatus int, reason, message string) *Status

func WrapStatus

func WrapStatus(err error, httpStatus int, message string) *Status

WrapStatus wraps an error with automatically derived reason from the HTTP status code.

func WrapStatusf

func WrapStatusf(err error, httpStatus int, format string, a ...any) *Status

WrapStatusf wraps an error with automatically derived reason and formatted message.

func Wrapf

func Wrapf(err error, httpStatus int, reason, format string, a ...any) *Status

func (*Status) Cause

func (s *Status) Cause() error

func (*Status) Err

func (s *Status) Err() error

Err converts the Status to an error interface.

The returned error type is either:

  • nil: When StatusCode() is 2xx
  • *StatusError: An error that implements the error interface

func (*Status) FieldViolations

func (s *Status) FieldViolations() []*FieldViolation

func (*Status) GetLocalizedMessage

func (s *Status) GetLocalizedMessage() *LocalizedMessage

func (*Status) Localized

func (s *Status) Localized() *Localized

func (*Status) Message

func (s *Status) Message() string

func (*Status) Metadata

func (s *Status) Metadata() map[string]string

func (*Status) Reason

func (s *Status) Reason() string

func (*Status) StatusCode

func (s *Status) StatusCode() int

func (*Status) String

func (s *Status) String() string

func (*Status) ToFieldViolations

func (s *Status) ToFieldViolations(field string) FieldViolations

ToFieldViolations converts this Status to field violations for the specified field

func (*Status) Translated

func (s *Status) Translated(ib *i18nx.I18N, lang language.Tag) *Status

Translated returns a new Status with translated messages stored in localizedMessage fields. The original message is preserved unchanged.

func (*Status) WithCause

func (s *Status) WithCause(cause error) *Status

func (*Status) WithFieldViolations

func (s *Status) WithFieldViolations(fieldViolations ...*FieldViolation) *Status

WithFieldViolations adds multiple field-level validation violations in batch. Multiple violations for the same field are allowed and will be appended.

func (*Status) WithFlattenFieldViolations

func (s *Status) WithFlattenFieldViolations(inputs ...any) *Status

WithFlattenFieldViolations accepts various types of field violation inputs and flattens them. Supports *FieldViolation, []*FieldViolation, FieldViolations. Mixed types are allowed in a single call for maximum flexibility.

Note: For error and *Status inputs, use ToFieldViolations(err, field) or status.ToFieldViolations(field) first to specify the field name, then pass the result to this function.

func (*Status) WithLocalized

func (s *Status) WithLocalized(key string, args ...any) *Status

func (*Status) WithLocalizedArgs

func (s *Status) WithLocalizedArgs(args ...any) *Status

WithLocalizedArgs sets template arguments for i18n. This method relies on the constructor invariant that localized is initialized with the reason as the default key. Use WithLocalized if you need to change the translation key before setting args.

func (*Status) WithMessage

func (s *Status) WithMessage(message string) *Status

func (*Status) WithMessagef

func (s *Status) WithMessagef(format string, a ...any) *Status

func (*Status) WithMetadata

func (s *Status) WithMetadata(md map[string]string) *Status

func (*Status) WithReason

func (s *Status) WithReason(reason string) *Status

func (*Status) WithStatusCode

func (s *Status) WithStatusCode(httpStatus int) *Status

type StatusError

type StatusError struct {
	// contains filtered or unexported fields
}

func (*StatusError) Cause

func (e *StatusError) Cause() error

func (*StatusError) Error

func (e *StatusError) Error() string

func (*StatusError) Format

func (e *StatusError) Format(s fmt.State, verb rune)

func (*StatusError) Is

func (e *StatusError) Is(target error) bool

Is compares two StatusErrors by httpStatus + reason (decision: scheme A).

func (*StatusError) Status

func (e *StatusError) Status() *Status

func (*StatusError) Unwrap

func (e *StatusError) Unwrap() error

type Validator

type Validator interface {
	Validate() error
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL