error.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. package rest
  2. import (
  3. "net/http"
  4. )
  5. // REST API error.
  6. var (
  7. Err = Error{}
  8. ErrMovedPermanently = NewError(http.StatusMovedPermanently, "") // 301
  9. ErrFound = NewError(http.StatusFound, "") // 302
  10. ErrTemporaryRedirect = NewError(http.StatusTemporaryRedirect, "") // 307
  11. ErrPermanentRedirect = NewError(http.StatusPermanentRedirect, "") // 308
  12. ErrBadRequest = NewError(http.StatusBadRequest, "") // 400
  13. ErrUnauthorized = NewError(http.StatusUnauthorized, "") // 401
  14. ErrPaymentRequired = NewError(http.StatusPaymentRequired, "") // 402
  15. ErrForbidden = NewError(http.StatusForbidden, "") // 403
  16. ErrNotFound = NewError(http.StatusNotFound, "") // 404
  17. ErrMethodNotAllowed = NewError(http.StatusMethodNotAllowed, "") // 405
  18. ErrNotAcceptable = NewError(http.StatusNotAcceptable, "") // 406
  19. ErrInternalServerError = NewError(http.StatusInternalServerError, "") // 500
  20. ErrNotImplemented = NewError(http.StatusNotImplemented, "") // 501
  21. ErrBadGateway = NewError(http.StatusBadGateway, "") // 502
  22. ErrServiceUnavailable = NewError(http.StatusServiceUnavailable, "") // 503
  23. ErrGatewayTimeout = NewError(http.StatusGatewayTimeout, "") // 504
  24. )
  25. // Error represents a REST API error.
  26. // It can be marshaled to JSON with ease and provides a standard format for printing errors and additional data.
  27. type Error struct {
  28. StatusCode int `json:"-"` // HTTP status code (200, 404, 500 etc.)
  29. Message string `json:"message"` // Status message ("OK", "Not found", "Internal server error" etc.)
  30. Data map[string]interface{} `json:"data,omitempty"` // Optional additional data.
  31. }
  32. // Error retrieves the message of a REST API error.
  33. // If it has a "error" string attached using WithData or WithError, that message is returned.
  34. // Otherwise, the Error's own message is returned.
  35. func (e Error) Error() string {
  36. if e.Data != nil && e.Data["error"] != nil {
  37. if value, ok := e.Data["error"].(string); ok {
  38. return value
  39. }
  40. }
  41. return e.Message
  42. }
  43. // Is determines whether the Error is an instance of the target.
  44. // https://pkg.go.dev/errors#Is
  45. //
  46. // If the target is a REST API error and specifies a status code, this function returns true if the status codes match.
  47. // If the target is an empty REST API error, this function always returns true.
  48. func (e Error) Is(target error) bool {
  49. if t, ok := target.(Error); ok {
  50. return t.StatusCode == e.StatusCode || t.StatusCode == 0
  51. }
  52. return false
  53. }
  54. // WithData returns a copy of the HTTP error with the given data merged in.
  55. func (e Error) WithData(data map[string]interface{}) Error {
  56. if e.Data == nil {
  57. e.Data = map[string]any{}
  58. }
  59. if data != nil {
  60. for key, value := range data {
  61. e.Data[key] = value
  62. }
  63. }
  64. return e
  65. }
  66. // WithError returns a copy of the HTTP error with the given error's message merged in to its additional data.
  67. func (e Error) WithError(err error) Error {
  68. return e.WithData(map[string]interface{}{
  69. "error": err.Error(),
  70. })
  71. }
  72. // WithMessage returns a copy of the HTTP error with the given message.
  73. func (e Error) WithMessage(message string) Error {
  74. e.Message = message
  75. return e
  76. }
  77. // WithValue returns a copy of the HTTP error with a single data value added.
  78. func (e Error) WithValue(name string, value any) Error {
  79. return e.WithData(map[string]any{
  80. name: value,
  81. })
  82. }
  83. // Write writes the HTTP error to an HTTP response as plain text.
  84. // Additional data is omitted.
  85. func (e Error) Write(w http.ResponseWriter) (int, error) {
  86. if e.StatusCode == 0 {
  87. e.StatusCode = 200
  88. }
  89. w.WriteHeader(e.StatusCode)
  90. return w.Write([]byte(e.Message))
  91. }
  92. // WriteJSON writes the HTTP error to an HTTP response as JSON.
  93. func (e Error) WriteJSON(w http.ResponseWriter) error {
  94. if e.StatusCode == 0 {
  95. e.StatusCode = 200
  96. }
  97. return WriteResponseJSON(w, e.StatusCode, e)
  98. }
  99. // NewError creates a new REST API error.
  100. // If the message is empty, the standard text provided by http.StatusText is substituted.
  101. func NewError(statusCode int, message string) Error {
  102. if len(message) == 0 {
  103. message = http.StatusText(statusCode)
  104. }
  105. return Error{
  106. StatusCode: statusCode,
  107. Message: message,
  108. }
  109. }