error.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  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. t, ok := target.(Error)
  50. if !ok {
  51. return false
  52. }
  53. if t.StatusCode == 0 {
  54. return true
  55. }
  56. return t.StatusCode == e.StatusCode
  57. }
  58. // WithData returns a copy of the HTTP error with the given data merged in.
  59. func (e Error) WithData(data map[string]interface{}) Error {
  60. if e.Data == nil {
  61. e.Data = map[string]any{}
  62. }
  63. if data != nil {
  64. for key, value := range data {
  65. e.Data[key] = value
  66. }
  67. }
  68. return e
  69. }
  70. // WithError returns a copy of the HTTP error with the given error's message merged in to its additional data.
  71. func (e Error) WithError(err error) Error {
  72. return e.WithData(map[string]interface{}{
  73. "error": err.Error(),
  74. })
  75. }
  76. // WithMessage returns a copy of the HTTP error with the given message.
  77. func (e Error) WithMessage(message string) Error {
  78. e.Message = message
  79. return e
  80. }
  81. // WithValue returns a copy of the HTTP error with a single data value added.
  82. func (e Error) WithValue(name string, value any) Error {
  83. return e.WithData(map[string]any{
  84. name: value,
  85. })
  86. }
  87. // Write writes the HTTP error to an HTTP response as plain text.
  88. // Additional data is omitted.
  89. func (e Error) Write(w http.ResponseWriter) (int, error) {
  90. if e.StatusCode == 0 {
  91. e.StatusCode = 200
  92. }
  93. w.WriteHeader(e.StatusCode)
  94. return w.Write([]byte(e.Message))
  95. }
  96. // WriteJSON writes the HTTP error to an HTTP response as JSON.
  97. func (e Error) WriteJSON(w http.ResponseWriter) error {
  98. if e.StatusCode == 0 {
  99. e.StatusCode = 200
  100. }
  101. return WriteResponseJSON(w, e.StatusCode, e)
  102. }
  103. // NewError creates a new REST API error.
  104. // If the message is empty, the standard text provided by http.StatusText is substituted.
  105. func NewError(statusCode int, message string) Error {
  106. if len(message) == 0 {
  107. message = http.StatusText(statusCode)
  108. }
  109. return Error{
  110. StatusCode: statusCode,
  111. Message: message,
  112. }
  113. }