feat(errors): 统一 AppError(Code/Message/Cause/HTTPCode + Wrap %w + HTTPStatus + ToJSON {error:{code,message}})
This commit is contained in:
1 parent
ebeecc7a32
commit
7f7cb8fbf7
4 files changed
+420
No files matched your search
@@ -0,0 +1,45 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
stdErrors "errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// HTTPStatus resolves the HTTP status code for err.
|
||||
//
|
||||
// If err (or anything wrapped by it) is an AppError with a non-zero HTTPCode,
|
||||
// that code is returned; otherwise the fallback is http.StatusInternalServerError
|
||||
// (500). New and Wrap propagate the cause's status, so wrapping a sentinel such
|
||||
// as ErrNotFound keeps status 404.
|
||||
func HTTPStatus(err error) int {
|
||||
var appErr *AppError
|
||||
if stdErrors.As(err, &appErr) && appErr.HTTPCode != 0 {
|
||||
return appErr.HTTPCode
|
||||
}
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
|
||||
// ToJSON renders err as an RFC 7807 style error object, aligned with the client
|
||||
// API contract {error:{code,message}}.
|
||||
//
|
||||
// For an AppError (or one wrapped by it) the Code and Message are returned
|
||||
// verbatim. For any other error a generic internal error is returned so that
|
||||
// internal details are never leaked to the user; the underlying error is kept
|
||||
// only in the Cause field for logging.
|
||||
func ToJSON(err error) map[string]any {
|
||||
var appErr *AppError
|
||||
if stdErrors.As(err, &appErr) {
|
||||
return map[string]any{
|
||||
"error": map[string]any{
|
||||
"code": appErr.Code,
|
||||
"message": appErr.Message,
|
||||
},
|
||||
}
|
||||
}
|
||||
return map[string]any{
|
||||
"error": map[string]any{
|
||||
"code": ErrInternal.Code,
|
||||
"message": ErrInternal.Message,
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user