error.go 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. package rest
  2. import (
  3. "net/http"
  4. )
  5. // Error represents a REST API error.
  6. // It can be marshaled to JSON with ease.
  7. type Error struct {
  8. StatusCode int `json:"statusCode"` // HTTP status code (200, 404, 500 etc.)
  9. Message string `json:"message"` // Status message ("OK", "Not found", "Internal server error" etc.)
  10. Data map[string]interface{} `json:"data,omitempty"` // Optional additional data.
  11. }
  12. // Error retrieves the message of a REST API Error.
  13. // If it has a "error" string attached using WithData or WithError, that message is returned.
  14. // Otherwise, the Error's own message is returned.
  15. func (e Error) Error() string {
  16. if e.Data != nil && e.Data["error"] != nil {
  17. if value, ok := e.Data["error"].(string); ok {
  18. return value
  19. }
  20. }
  21. return e.Message
  22. }
  23. // Is determines whether the Error is an instance of the target.
  24. // https://pkg.go.dev/errors#Is
  25. //
  26. // This function supports matching both any error and a specific error, based on the status code.
  27. // Use rest.Err (an empty error) for the former:
  28. //
  29. // errors.Is(err, rest.Err) // True if any REST API Error
  30. // errors.Is(err, rest.ErrBadRequest) // True if error is REST 400 Bad Request
  31. func (e Error) Is(target error) bool {
  32. t, ok := target.(Error)
  33. if !ok {
  34. return false
  35. }
  36. if t.StatusCode == 0 {
  37. return true
  38. }
  39. return t.StatusCode == e.StatusCode
  40. }
  41. // WithData returns a copy of the HTTP error with the given data merged in.
  42. func (e Error) WithData(data map[string]interface{}) Error {
  43. if e.Data == nil {
  44. e.Data = map[string]any{}
  45. }
  46. if data != nil {
  47. for key, value := range data {
  48. e.Data[key] = value
  49. }
  50. }
  51. return e
  52. }
  53. // WithError returns a copy of the HTTP error with the given error's message merged in to its additional data.
  54. func (e Error) WithError(err error) Error {
  55. return e.WithData(map[string]interface{}{
  56. "error": err.Error(),
  57. })
  58. }
  59. // WithMessage returns a copy of the HTTP error with the given message.
  60. func (e Error) WithMessage(message string) Error {
  61. e.Message = message
  62. return e
  63. }
  64. // WithValue returns a copy of the HTTP error with a single data value added.
  65. func (e Error) WithValue(name string, value any) Error {
  66. return e.WithData(map[string]any{
  67. name: value,
  68. })
  69. }
  70. // Write writes the HTTP error to an HTTP response as plain text.
  71. // Additional data is omitted.
  72. func (e Error) Write(w http.ResponseWriter) {
  73. w.WriteHeader(e.StatusCode)
  74. w.Write([]byte(e.Message))
  75. }
  76. // WriteJSON writes the HTTP error to an HTTP response as JSON.
  77. func (e Error) WriteJSON(w http.ResponseWriter) error {
  78. return WriteResponseJSON(w, e.StatusCode, e)
  79. }