Documentation
¶
Overview ¶
Package response provides unified HTTP JSON envelopes, application errors, status code conventions, and i18n message key constants for ling-base APIs.
Success envelope ¶
{"code": 200, "msg": "success", "data": ...}
Error envelope ¶
{"code": 1001, "msg": "Not found", "error": "NOT_FOUND", "data": null}
Service layers return *AppError; HTTP handlers render it once via the gin helpers in the response/gin subpackage or via ErrorEnvelope.
i18n ¶
AppError stores an i18n message key (MsgKey) and optional format arguments (MsgArgs). At render time, a MessageResolver translates the key to a localized string. The response/gin subpackage integrates with the i18n.Manager to resolve messages based on the request locale.
Error codes ¶
Two parallel code systems are used:
- Code (string): stable identifier like "NOT_FOUND" — clients branch on this.
- Numeric code (int): business code like 1001 — returned in the "code" field of the envelope.
See codes.go for the full constant set and keys.go for i18n keys.
Index ¶
- Constants
- func EnvelopeData(data any) map[string]any
- func EnvelopeError(msg string) map[string]any
- func ErrCodeFor(code Code) int
- func FormatDetails(ae *AppError) string
- func HTTPStatusFor(code Code) int
- func HTTPStatusOf(ae *AppError) int
- func I18nKeyFor(code Code) string
- func IsAppError(err error) bool
- type AppError
- func AsAppError(err error) *AppError
- func Err(code Code) *AppError
- func From(err error) *AppError
- func New(code Code, message string) *AppError
- func NewI18n(code Code, msgKey string, args ...any) *AppError
- func Newf(code Code, format string, args ...any) *AppError
- func Wrap(code Code, message string, cause error) *AppError
- func WrapErr(code Code, cause error) *AppError
- func WrapI18n(code Code, msgKey string, cause error, args ...any) *AppError
- func (e *AppError) Error() string
- func (e *AppError) Is(target error) bool
- func (ae *AppError) NumCode() int
- func (e *AppError) Unwrap() error
- func (e *AppError) WithArg(arg any) *AppError
- func (e *AppError) WithArgs(args ...any) *AppError
- func (e *AppError) WithCause(cause error) *AppError
- func (e *AppError) WithDetails(d map[string]any) *AppError
- func (e *AppError) WithStatus(status int) *AppError
- type ChainResolver
- type Code
- type ErrorResponse
- type MessageResolver
- type Page
- type ResolverFunc
- type Response
- type StaticResolver
Constants ¶
const ( CodeSuccess = 200 // Business errors (1000-1999) CodeNumInvalidParams = 1000 CodeNumNotFound = 1001 CodeNumForbidden = 1003 CodeNumConflict = 1004 CodeNumRateLimited = 1005 CodeNumTenantMismatch = 1006 CodeNumQuotaExceeded = 1007 CodeNumUpstreamTimeout = 1008 CodeNumDuplicate = 1010 CodeNumValidationFailed = 1011 // Auth errors (1100-1199) CodeNumInvalidCredentials = 1100 CodeNumMissingToken = 1104 CodeNumInvalidToken = 1105 // Tenant errors (1200-1299) CodeNumRegisterDisabled = 1200 CodeNumEmailExists = 1201 CodeNumTenantNotFound = 1203 CodeNumTenantSuspended = 1204 // Permission errors (1300-1399) CodeNumPermInsufficient = 1300 // System errors (2000-2999) CodeNumInternal = 2000 CodeNumProviderErr = 2002 )
Numeric business codes returned in the "code" field of envelopes. 200 = success; 1000-1999 = client/business errors; 2000-2999 = system errors. Ranges above 3000 are reserved for application-specific codes.
const ( // common.* — generic success/error messages KeySuccess = "common.success" KeyCreated = "common.created" KeyUpdated = "common.updated" KeyDeleted = "common.deleted" KeyInvalidParams = "common.invalid_params" KeyInvalidBody = "common.invalid_body" KeyNotFound = "common.not_found" KeyForbidden = "common.forbidden" KeyConflict = "common.conflict" KeyRateLimited = "common.rate_limited" KeyInternalError = "common.internal_error" KeyQuotaExceeded = "common.quota_exceeded" KeyUpstreamTimeout = "common.upstream_timeout" KeyTenantMismatch = "common.tenant_mismatch" KeyDuplicate = "common.duplicate" // auth.* — authentication KeyAuthInvalidCredentials = "auth.invalid_credentials" KeyAuthMissingToken = "auth.missing_token" KeyAuthInvalidToken = "auth.invalid_token" KeyAuthEmailNotRegistered = "auth.email_not_registered" KeyAuthEmailSameAsCurrent = "auth.email_same_as_current" // tenant.* — tenant/organization KeyTenantRegisterDisabled = "tenant.register_disabled" KeyTenantEmailExists = "tenant.email_exists" KeyTenantNotFound = "tenant.not_found" KeyTenantSuspended = "tenant.suspended" // perm.* — permissions KeyPermInsufficient = "perm.insufficient" // validation.* — field validation KeyValidationRequired = "validation.required" KeyValidationEmail = "validation.email" KeyValidationMin = "validation.min" KeyValidationMax = "validation.max" KeyValidationUsernameShort = "validation.username_short" KeyValidationUsernameFormat = "validation.username_format" KeyValidationPasswordShort = "validation.password_short" KeyValidationCaptchaRequired = "validation.captcha_required" KeyValidationCaptchaInvalid = "validation.captcha_invalid" )
Message key constants for i18n. These follow the dotted convention "domain.message" and are intended to be resolved by an i18n.Manager or any MessageResolver implementation.
Using keys (not hardcoded text) in errors lets the rendering layer localize messages at the last moment based on the request locale.
Variables ¶
This section is empty.
Functions ¶
func EnvelopeData ¶
EnvelopeData wraps any value in a map under the "data" key. This is a convenience for handlers that need to return extra metadata.
func EnvelopeError ¶
EnvelopeError wraps an error message in a simple map for ad-hoc use.
func ErrCodeFor ¶
ErrCodeFor returns the numeric business code for a string Code.
func FormatDetails ¶
FormatDetails converts Details to a flat "key=value" string for logging. Returns empty string if Details is nil or empty.
func HTTPStatusFor ¶
HTTPStatusFor returns the default HTTP status code for a Code.
func HTTPStatusOf ¶
HTTPStatusOf returns the HTTP status for an AppError, using the explicit override if set, otherwise the default for the Code.
func I18nKeyFor ¶
I18nKeyFor returns the default i18n message key for a Code. The key can be passed to a MessageResolver to obtain a localized string.
Types ¶
type AppError ¶
type AppError struct {
Code Code // Stable string identifier (e.g. "NOT_FOUND")
MsgKey string // i18n message key (e.g. "common.not_found")
MsgArgs []any // Arguments for i18n message formatting
Message string // Direct message (non-i18n fallback)
HTTPStatus int // HTTP status code (0 = auto from Code)
Cause error // Wrapped underlying error
Details map[string]any // Additional structured details
}
AppError is the unified application error shape. Service layers return *AppError; HTTP handlers render it once via the gin helpers or Envelope/HTTPStatusOf.
AppError supports two message paths:
- i18n path: MsgKey + MsgArgs are resolved at render time via a MessageResolver (e.g. i18n.Manager). This is the recommended path for user-facing errors.
- direct path: Message is used as-is when MsgKey is empty or no resolver is available.
func AsAppError ¶
AsAppError is a convenience that wraps From and returns the result even for nil errors (returning a generic internal error). Useful when a non-nil *AppError is required.
func Err ¶
Err creates an AppError from a Code only. The user-facing text is resolved at render time using the default i18n key for the Code.
func From ¶
From converts any error to *AppError. If err is already an *AppError it is returned as-is. nil input returns nil. Unknown errors become CodeInternal.
func NewI18n ¶
NewI18n constructs an AppError whose user-facing text is resolved at render time via a MessageResolver.
func WrapErr ¶
WrapErr creates an AppError from a code and underlying error. The user-facing text is resolved at render time.
func (*AppError) Error ¶
Error returns a human-readable string. If Message is set it is used; otherwise the MsgKey is returned so there is always a non-empty value.
func (*AppError) Is ¶
Is reports whether target is an *AppError with the same Code. This enables errors.Is(err, response.Err(response.CodeNotFound)).
func (*AppError) WithDetails ¶
WithDetails attaches structured details to the error.
func (*AppError) WithStatus ¶
WithStatus overrides the HTTP status code.
type ChainResolver ¶
type ChainResolver struct {
Resolvers []MessageResolver
}
ChainResolver tries each resolver in order, returning the first non-empty result. If all resolvers return empty, the key is returned.
type Code ¶
type Code string
Code is a stable, human-readable business error identifier. Clients branch on the string value; do not reuse a Code's semantics across releases.
const ( CodeBadRequest Code = "BAD_REQUEST" CodeForbidden Code = "FORBIDDEN" CodeNotFound Code = "NOT_FOUND" CodeConflict Code = "CONFLICT" CodeDuplicate Code = "DUPLICATE" CodeRateLimited Code = "RATE_LIMITED" CodeQuotaExceeded Code = "QUOTA_EXCEEDED" CodeValidation Code = "VALIDATION_FAILED" CodeTenantMismatch Code = "TENANT_MISMATCH" CodeAuthFailed Code = "AUTH_FAILED" CodeCredentialInvalid Code = "CREDENTIAL_INVALID" CodeUpstreamTimeout Code = "UPSTREAM_TIMEOUT" CodeProviderError Code = "PROVIDER_ERROR" CodeInternal Code = "INTERNAL" )
Standard stable string codes. These are the canonical identifiers returned in the "error" field of error envelopes.
func CodeForHTTPStatus ¶
CodeForHTTPStatus returns the default string Code for an HTTP status.
type ErrorResponse ¶
type ErrorResponse struct {
Code int `json:"code"` // numeric business code
Message string `json:"msg"` // localized or direct message
Error string `json:"error"` // stable string Code
Data any `json:"data"` // optional payload (usually nil)
Details map[string]any `json:"details,omitempty"`
}
ErrorResponse is the standard JSON error envelope.
{"code": 1001, "msg": "Not found", "error": "NOT_FOUND", "data": null, "details": null}
func ErrorEnvelope ¶
func ErrorEnvelope(ae *AppError, resolver MessageResolver) *ErrorResponse
ErrorEnvelope builds an ErrorResponse from an AppError using a MessageResolver to localize the message. If resolver is nil, the AppError.Message or MsgKey is used as the message.
type MessageResolver ¶
MessageResolver resolves an i18n message key to a localized string. Implementations are typically backed by an i18n.Manager, but any source (static map, database, remote service) can satisfy this interface.
The args are optional format arguments. If the resolved template contains verbs (e.g. %d, %s), the implementation should apply them.
var NoopResolver MessageResolver = ResolverFunc(func(key string, _ ...any) string {
return key
})
NoopResolver always returns the key unchanged. It is the default when no resolver is configured.
type Page ¶
type Page struct {
List any `json:"list"`
Total int64 `json:"total"`
Page int `json:"page"`
Size int `json:"size"`
TotalPage int `json:"total_page"`
}
Page is a paginated list payload, intended to be embedded in Response.Data.
{"code": 200, "msg": "success", "data": {"list": [...], "total": 100, "page": 1, "size": 20, "total_page": 5}}
type ResolverFunc ¶
ResolverFunc is a function adapter for MessageResolver.
type Response ¶
Response is the standard JSON success envelope.
{"code": 200, "msg": "success", "data": ...}
func SuccessMsg ¶
SuccessMsg builds a success Response with a custom message.
type StaticResolver ¶
StaticResolver is a simple map-backed resolver. It is useful for testing and for applications that do not need full i18n support.
Missing keys fall back to the key itself. If the resolved template contains format verbs and args are provided, fmt.Sprintf is applied.