Skip to content

Commit b25cbdc

Browse files
feat: added additional error types (#146)
* fix: added http error codes * chore: added error tests * fix: replaced ifs with switch case - statement
1 parent 9eedca9 commit b25cbdc

3 files changed

Lines changed: 360 additions & 54 deletions

File tree

errors/errors.go

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,34 @@
11
package errors
22

3+
import "net/http"
4+
5+
type StatusCoder interface {
6+
error
7+
StatusCode() int
8+
}
9+
10+
// HTTPError is a generic composable error for arbitrary HTTP status codes.
11+
type HTTPError struct {
12+
Message string
13+
Code int
14+
}
15+
16+
func (e *HTTPError) Error() string {
17+
return e.Message
18+
}
19+
20+
func (e *HTTPError) StatusCode() int {
21+
if e.Code == 0 {
22+
return http.StatusInternalServerError
23+
}
24+
25+
return e.Code
26+
}
27+
28+
func NewHTTPError(code int, message string) *HTTPError {
29+
return &HTTPError{Message: message, Code: code}
30+
}
31+
332
type BadRequest struct {
433
Message string
534
}
@@ -8,6 +37,10 @@ func (e *BadRequest) Error() string {
837
return e.Message
938
}
1039

40+
func (e *BadRequest) StatusCode() int {
41+
return http.StatusBadRequest
42+
}
43+
1144
type NotFound struct {
1245
Message string
1346
}
@@ -16,6 +49,10 @@ func (e *NotFound) Error() string {
1649
return e.Message
1750
}
1851

52+
func (e *NotFound) StatusCode() int {
53+
return http.StatusNotFound
54+
}
55+
1956
type ServiceUnavailable struct {
2057
Message string
2158
}
@@ -24,6 +61,10 @@ func (e *ServiceUnavailable) Error() string {
2461
return e.Message
2562
}
2663

64+
func (e *ServiceUnavailable) StatusCode() int {
65+
return http.StatusServiceUnavailable
66+
}
67+
2768
type Forbidden struct {
2869
Message string
2970
}
@@ -32,10 +73,150 @@ func (e *Forbidden) Error() string {
3273
return e.Message
3374
}
3475

76+
func (e *Forbidden) StatusCode() int {
77+
return http.StatusForbidden
78+
}
79+
3580
type Unauthorized struct {
3681
Message string
3782
}
3883

3984
func (e *Unauthorized) Error() string {
4085
return e.Message
4186
}
87+
88+
func (e *Unauthorized) StatusCode() int {
89+
return http.StatusUnauthorized
90+
}
91+
92+
type MethodNotAllowed struct {
93+
Message string
94+
}
95+
96+
func (e *MethodNotAllowed) Error() string {
97+
return e.Message
98+
}
99+
100+
func (e *MethodNotAllowed) StatusCode() int {
101+
return http.StatusMethodNotAllowed
102+
}
103+
104+
type Conflict struct {
105+
Message string
106+
}
107+
108+
func (e *Conflict) Error() string {
109+
return e.Message
110+
}
111+
112+
func (e *Conflict) StatusCode() int {
113+
return http.StatusConflict
114+
}
115+
116+
type Gone struct {
117+
Message string
118+
}
119+
120+
func (e *Gone) Error() string {
121+
return e.Message
122+
}
123+
124+
func (e *Gone) StatusCode() int {
125+
return http.StatusGone
126+
}
127+
128+
type UnsupportedMediaType struct {
129+
Message string
130+
}
131+
132+
func (e *UnsupportedMediaType) Error() string {
133+
return e.Message
134+
}
135+
136+
func (e *UnsupportedMediaType) StatusCode() int {
137+
return http.StatusUnsupportedMediaType
138+
}
139+
140+
type UnprocessableEntity struct {
141+
Message string
142+
}
143+
144+
func (e *UnprocessableEntity) Error() string {
145+
return e.Message
146+
}
147+
148+
func (e *UnprocessableEntity) StatusCode() int {
149+
return http.StatusUnprocessableEntity
150+
}
151+
152+
type TooManyRequests struct {
153+
Message string
154+
}
155+
156+
func (e *TooManyRequests) Error() string {
157+
return e.Message
158+
}
159+
160+
func (e *TooManyRequests) StatusCode() int {
161+
return http.StatusTooManyRequests
162+
}
163+
164+
type InternalServerError struct {
165+
Message string
166+
}
167+
168+
func (e *InternalServerError) Error() string {
169+
return e.Message
170+
}
171+
172+
func (e *InternalServerError) StatusCode() int {
173+
return http.StatusInternalServerError
174+
}
175+
176+
type BadGateway struct {
177+
Message string
178+
}
179+
180+
func (e *BadGateway) Error() string {
181+
return e.Message
182+
}
183+
184+
func (e *BadGateway) StatusCode() int {
185+
return http.StatusBadGateway
186+
}
187+
188+
type GatewayTimeout struct {
189+
Message string
190+
}
191+
192+
func (e *GatewayTimeout) Error() string {
193+
return e.Message
194+
}
195+
196+
func (e *GatewayTimeout) StatusCode() int {
197+
return http.StatusGatewayTimeout
198+
}
199+
200+
type RequestTimeout struct {
201+
Message string
202+
}
203+
204+
func (e *RequestTimeout) Error() string {
205+
return e.Message
206+
}
207+
208+
func (e *RequestTimeout) StatusCode() int {
209+
return http.StatusRequestTimeout
210+
}
211+
212+
type NotImplemented struct {
213+
Message string
214+
}
215+
216+
func (e *NotImplemented) Error() string {
217+
return e.Message
218+
}
219+
220+
func (e *NotImplemented) StatusCode() int {
221+
return http.StatusNotImplemented
222+
}

middlewares/error_handler.go

Lines changed: 58 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -13,74 +13,78 @@ import (
1313

1414
type ErrorHandler struct{}
1515

16+
func writeError(w http.ResponseWriter, r *http.Request, statusCode int, message string) {
17+
render.Status(r, statusCode)
18+
render.JSON(w, r, types.ErrorResponse{
19+
Status: http.StatusText(statusCode),
20+
Error: message,
21+
})
22+
}
23+
1624
func (e *ErrorHandler) Wrap(handler func(w http.ResponseWriter, r *http.Request) error) http.HandlerFunc {
1725
return func(w http.ResponseWriter, r *http.Request) {
1826
var notFoundError *serviceErrors.NotFound
1927
var badRequestError *serviceErrors.BadRequest
2028
var serviceUnavailable *serviceErrors.ServiceUnavailable
2129
var forbiddenError *serviceErrors.Forbidden
2230
var unauthorizedError *serviceErrors.Unauthorized
31+
var methodNotAllowedError *serviceErrors.MethodNotAllowed
32+
var conflictError *serviceErrors.Conflict
33+
var goneError *serviceErrors.Gone
34+
var unsupportedMediaTypeError *serviceErrors.UnsupportedMediaType
35+
var unprocessableEntityError *serviceErrors.UnprocessableEntity
36+
var tooManyRequestsError *serviceErrors.TooManyRequests
37+
var internalServerError *serviceErrors.InternalServerError
38+
var badGatewayError *serviceErrors.BadGateway
39+
var gatewayTimeoutError *serviceErrors.GatewayTimeout
40+
var requestTimeoutError *serviceErrors.RequestTimeout
41+
var notImplementedError *serviceErrors.NotImplemented
2342

2443
err := handler(w, r)
25-
26-
if (errors.As(err, &notFoundError)) || (errors.Is(err, storage.ErrNotFound)) {
27-
render.Status(r, http.StatusNotFound)
28-
response := types.ErrorResponse{
29-
Status: http.StatusText(http.StatusNotFound),
30-
Error: err.Error(),
31-
}
32-
render.JSON(w, r, response)
33-
return
34-
}
35-
36-
if errors.As(err, &badRequestError) {
37-
render.Status(r, http.StatusBadRequest)
38-
response := types.ErrorResponse{
39-
Status: http.StatusText(http.StatusBadRequest),
40-
Error: err.Error(),
41-
}
42-
render.JSON(w, r, response)
43-
return
44-
}
45-
46-
if errors.As(err, &serviceUnavailable) {
47-
render.Status(r, http.StatusServiceUnavailable)
48-
response := types.ErrorResponse{
49-
Status: http.StatusText(http.StatusServiceUnavailable),
50-
Error: err.Error(),
51-
}
52-
render.JSON(w, r, response)
44+
if err == nil {
5345
return
5446
}
5547

56-
if errors.As(err, &forbiddenError) {
57-
render.Status(r, http.StatusForbidden)
58-
response := types.ErrorResponse{
59-
Status: http.StatusText(http.StatusForbidden),
60-
Error: err.Error(),
61-
}
62-
render.JSON(w, r, response)
63-
return
64-
}
48+
statusCode := http.StatusInternalServerError
49+
responseError := err.Error()
6550

66-
if errors.As(err, &unauthorizedError) {
67-
render.Status(r, http.StatusUnauthorized)
68-
response := types.ErrorResponse{
69-
Status: http.StatusText(http.StatusUnauthorized),
70-
Error: err.Error(),
71-
}
72-
render.JSON(w, r, response)
73-
return
51+
switch {
52+
case errors.As(err, &notFoundError), errors.Is(err, storage.ErrNotFound):
53+
statusCode = http.StatusNotFound
54+
case errors.As(err, &badRequestError):
55+
statusCode = http.StatusBadRequest
56+
case errors.As(err, &serviceUnavailable):
57+
statusCode = http.StatusServiceUnavailable
58+
case errors.As(err, &forbiddenError):
59+
statusCode = http.StatusForbidden
60+
case errors.As(err, &unauthorizedError):
61+
statusCode = http.StatusUnauthorized
62+
case errors.As(err, &methodNotAllowedError):
63+
statusCode = http.StatusMethodNotAllowed
64+
case errors.As(err, &conflictError):
65+
statusCode = http.StatusConflict
66+
case errors.As(err, &goneError):
67+
statusCode = http.StatusGone
68+
case errors.As(err, &unsupportedMediaTypeError):
69+
statusCode = http.StatusUnsupportedMediaType
70+
case errors.As(err, &unprocessableEntityError):
71+
statusCode = http.StatusUnprocessableEntity
72+
case errors.As(err, &tooManyRequestsError):
73+
statusCode = http.StatusTooManyRequests
74+
case errors.As(err, &internalServerError):
75+
statusCode = http.StatusInternalServerError
76+
case errors.As(err, &badGatewayError):
77+
statusCode = http.StatusBadGateway
78+
case errors.As(err, &gatewayTimeoutError):
79+
statusCode = http.StatusGatewayTimeout
80+
case errors.As(err, &requestTimeoutError):
81+
statusCode = http.StatusRequestTimeout
82+
case errors.As(err, &notImplementedError):
83+
statusCode = http.StatusNotImplemented
84+
default:
85+
responseError = "encountered an unexpected server error: " + err.Error()
7486
}
7587

76-
if err != nil {
77-
render.Status(r, http.StatusInternalServerError)
78-
response := types.ErrorResponse{
79-
Status: http.StatusText(http.StatusInternalServerError),
80-
Error: "encountered an unexpected server error: " + err.Error(),
81-
}
82-
render.JSON(w, r, response)
83-
return
84-
}
88+
writeError(w, r, statusCode, responseError)
8589
}
8690
}

0 commit comments

Comments
 (0)