-
-
Notifications
You must be signed in to change notification settings - Fork 47
/
errors.go
151 lines (107 loc) · 5.29 KB
/
errors.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package fuego
import (
"errors"
"fmt"
"log/slog"
"net/http"
)
// ErrorWithStatus is an interface that can be implemented by an error to provide
// additional information about the error.
type ErrorWithStatus interface {
error
StatusCode() int
}
// HTTPError is the error response used by the serialization part of the framework.
type HTTPError struct {
Err error `json:"-" xml:"-"` // Developer readable error message. Not shown to the user to avoid security leaks.
Type string `json:"type,omitempty" xml:"type,omitempty" description:"URL of the error type. Can be used to lookup the error in a documentation"` // URL of the error type. Can be used to lookup the error in a documentation
Title string `json:"title,omitempty" xml:"title,omitempty" description:"Short title of the error"` // Short title of the error
Status int `json:"status,omitempty" xml:"status,omitempty" description:"HTTP status code" example:"403"` // HTTP status code. If using a different type than [HTTPError], for example [BadRequestError], this will be automatically overridden after Fuego error handling.
Detail string `json:"detail,omitempty" xml:"detail,omitempty" description:"Human readable error message"` // Human readable error message
Instance string `json:"instance,omitempty" xml:"instance,omitempty"`
Errors []ErrorItem `json:"errors,omitempty" xml:"errors,omitempty"`
}
type ErrorItem struct {
Name string `json:"name" xml:"name" description:"For example, name of the parameter that caused the error"`
Reason string `json:"reason" xml:"reason" description:"Human readable error message"`
More map[string]any `json:"more,omitempty" xml:"more,omitempty" description:"Additional information about the error"`
}
func (e HTTPError) Error() string {
title := e.Title
code := e.StatusCode()
if title == "" {
title = http.StatusText(code)
if title == "" {
title = "HTTP Error"
}
}
return fmt.Sprintf("%d %s: %s", code, title, e.Detail)
}
func (e HTTPError) StatusCode() int {
if e.Status == 0 {
return http.StatusInternalServerError
}
return e.Status
}
func (e HTTPError) Unwrap() error { return e.Err }
// BadRequestError is an error used to return a 400 status code.
type BadRequestError HTTPError
var _ ErrorWithStatus = BadRequestError{}
func (e BadRequestError) Error() string { return e.Err.Error() }
func (e BadRequestError) StatusCode() int { return http.StatusBadRequest }
func (e BadRequestError) Unwrap() error { return HTTPError(e) }
// NotFoundError is an error used to return a 404 status code.
type NotFoundError HTTPError
var _ ErrorWithStatus = NotFoundError{}
func (e NotFoundError) Error() string { return e.Err.Error() }
func (e NotFoundError) StatusCode() int { return http.StatusNotFound }
func (e NotFoundError) Unwrap() error { return HTTPError(e) }
// UnauthorizedError is an error used to return a 401 status code.
type UnauthorizedError HTTPError
var _ ErrorWithStatus = UnauthorizedError{}
func (e UnauthorizedError) Error() string { return e.Err.Error() }
func (e UnauthorizedError) StatusCode() int { return http.StatusUnauthorized }
func (e UnauthorizedError) Unwrap() error { return HTTPError(e) }
// ForbiddenError is an error used to return a 403 status code.
type ForbiddenError HTTPError
var _ ErrorWithStatus = ForbiddenError{}
func (e ForbiddenError) Error() string { return e.Err.Error() }
func (e ForbiddenError) StatusCode() int { return http.StatusForbidden }
func (e ForbiddenError) Unwrap() error { return HTTPError(e) }
// ConflictError is an error used to return a 409 status code.
type ConflictError HTTPError
var _ ErrorWithStatus = ConflictError{}
func (e ConflictError) Error() string { return e.Err.Error() }
func (e ConflictError) StatusCode() int { return http.StatusConflict }
func (e ConflictError) Unwrap() error { return HTTPError(e) }
// InternalServerError is an error used to return a 500 status code.
type InternalServerError = HTTPError
// NotAcceptableError is an error used to return a 406 status code.
type NotAcceptableError HTTPError
var _ ErrorWithStatus = NotAcceptableError{}
func (e NotAcceptableError) Error() string { return e.Err.Error() }
func (e NotAcceptableError) StatusCode() int { return http.StatusNotAcceptable }
func (e NotAcceptableError) Unwrap() error { return HTTPError(e) }
// ErrorHandler is the default error handler used by the framework.
// It transforms any error into the unified error type [HTTPError],
// Using the [ErrorWithStatus] interface.
func ErrorHandler(err error) error {
errResponse := HTTPError{
Err: err,
}
var errorInfo HTTPError
if errors.As(err, &errorInfo) {
errResponse = errorInfo
}
// Check status code
errResponse.Status = http.StatusInternalServerError
var errorStatus ErrorWithStatus
if errors.As(err, &errorStatus) {
errResponse.Status = errorStatus.StatusCode()
}
if errResponse.Title == "" {
errResponse.Title = http.StatusText(errResponse.Status)
}
slog.Error("Error "+errResponse.Title, "status", errResponse.StatusCode(), "detail", errResponse.Detail, "error", errResponse.Err)
return errResponse
}