error.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  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. func (e Error) Error() string {
  34. return e.Message
  35. }
  36. // Is determines whether the Error is an instance of the target.
  37. // https://pkg.go.dev/errors#Is
  38. //
  39. // If the target is a REST API error and specifies a status code, this function returns true if the status codes match.
  40. // If the target is an empty REST API error, this function always returns true.
  41. func (e Error) Is(target error) bool {
  42. if t, ok := target.(Error); ok {
  43. return t.StatusCode == e.StatusCode || t.StatusCode == 0
  44. }
  45. return false
  46. }
  47. // WithData returns a copy of the HTTP error with the given data merged in.
  48. func (e Error) WithData(data map[string]interface{}) Error {
  49. if e.Data == nil {
  50. e.Data = map[string]any{}
  51. }
  52. if data != nil {
  53. for key, value := range data {
  54. e.Data[key] = value
  55. }
  56. }
  57. return e
  58. }
  59. // WithError returns a copy of the HTTP error with the given error added either as the Message, if it empty, or as additional data.
  60. func (e Error) WithError(err error) Error {
  61. if e.Message == "" {
  62. return e.WithMessage(err.Error())
  63. }
  64. return e.WithData(map[string]interface{}{
  65. "error": err.Error(),
  66. })
  67. }
  68. // WithMessage returns a copy of the HTTP error with the given message.
  69. func (e Error) WithMessage(message string) Error {
  70. e.Message = message
  71. return e
  72. }
  73. // WithValue returns a copy of the HTTP error with a single data value added.
  74. func (e Error) WithValue(name string, value any) Error {
  75. return e.WithData(map[string]any{
  76. name: value,
  77. })
  78. }
  79. // Write writes the HTTP error to an HTTP response as plain text.
  80. // Additional data is omitted.
  81. func (e Error) Write(w http.ResponseWriter) (int, error) {
  82. if e.StatusCode == 0 {
  83. e.StatusCode = 200
  84. }
  85. w.WriteHeader(e.StatusCode)
  86. return w.Write([]byte(e.Message))
  87. }
  88. // WriteJSON writes the HTTP error to an HTTP response as JSON.
  89. func (e Error) WriteJSON(w http.ResponseWriter) error {
  90. if e.StatusCode == 0 {
  91. e.StatusCode = 200
  92. }
  93. return WriteResponseJSON(w, e.StatusCode, e)
  94. }
  95. // NewError creates a new REST API error.
  96. // If the message is empty, the standard text provided by http.StatusText is substituted.
  97. func NewError(statusCode int, message string) Error {
  98. if len(message) == 0 {
  99. message = http.StatusText(statusCode)
  100. }
  101. return Error{
  102. StatusCode: statusCode,
  103. Message: message,
  104. }
  105. }