1
1
package ginerr
2
2
3
3
import (
4
- "fmt "
4
+ "context "
5
5
"net/http"
6
+ "reflect"
6
7
)
7
8
8
9
const defaultCode = http .StatusInternalServerError
9
10
10
- // Deprecated: Please use v2 of this library
11
11
var DefaultErrorRegistry = NewErrorRegistry ()
12
12
13
13
type (
14
- internalHandler func (err error ) (int , any )
15
- internalStringHandler func (err string ) (int , any )
14
+ internalHandler func (ctx context. Context , err error ) (int , any )
15
+ internalStringHandler func (ctx context. Context , err string ) (int , any )
16
16
)
17
17
18
- // CustomErrorHandler is the template for unexported errors. For example binding.SliceValidationError
19
- // or uuid.invalidLengthError
20
- // Deprecated: Please use v2 of this library
21
- type CustomErrorHandler [R any ] func (err error ) (int , R )
22
-
23
- // ErrorStringHandler is the template for string errors that don't have their own object available. For example
24
- // "record not found" or "invalid input"
25
- // Deprecated: Please use v2 of this library
26
- type ErrorStringHandler [R any ] func (err string ) (int , R )
27
-
28
- // ErrorHandler is the template of an error handler in the ErrorRegistry. The E type is the error type that
29
- // the handler is registered for. The R type is the type of the response body.
30
- // Deprecated: Please use v2 of this library
31
- type ErrorHandler [E error , R any ] func (E ) (int , R )
32
-
33
- // Deprecated: Please use v2 of this library
34
18
func NewErrorRegistry () * ErrorRegistry {
35
19
registry := & ErrorRegistry {
36
20
handlers : make (map [string ]internalHandler ),
@@ -39,122 +23,137 @@ func NewErrorRegistry() *ErrorRegistry {
39
23
}
40
24
41
25
// Make sure the stringHandlers are available in the handlers
42
- registry .handlers ["* errors.errorString" ] = func (err error ) (int , any ) {
26
+ registry .handlers ["errors.errorString" ] = func (ctx context. Context , err error ) (int , any ) {
43
27
// Check if the error string exists
44
28
if handler , ok := registry .stringHandlers [err .Error ()]; ok {
45
- return handler (err .Error ())
29
+ return handler (ctx , err .Error ())
46
30
}
47
31
48
- return registry .DefaultCode , registry . DefaultResponse
32
+ return registry .defaultResponse ( ctx , err )
49
33
}
50
34
51
35
return registry
52
36
}
53
37
54
- // Deprecated: Please use v2 of this library
55
38
type ErrorRegistry struct {
56
39
// handlers are used when we know the type of the error
57
40
handlers map [string ]internalHandler
58
41
59
42
// stringHandlers are used when the error is only a string
60
43
stringHandlers map [string ]internalStringHandler
61
44
62
- // DefaultCode to return when no handler is found
45
+ // DefaultHandler takes precedent over DefaultCode and DefaultResponse
46
+ DefaultHandler func (ctx context.Context , err error ) (int , any )
47
+
48
+ // DefaultCode to return when no handler is found. Deprecated: Prefer DefaultHandler
63
49
DefaultCode int
64
50
65
- // DefaultResponse to return when no handler is found
51
+ // DefaultResponse to return when no handler is found. Deprecated: Prefer DefaultHandler
66
52
DefaultResponse any
67
53
}
68
54
69
- // Deprecated: Please use v2 of this library
55
+ // SetDefaultResponse is deprecated, prefer RegisterDefaultHandler
70
56
func (e * ErrorRegistry ) SetDefaultResponse (code int , response any ) {
71
57
e .DefaultCode = code
72
58
e .DefaultResponse = response
73
59
}
74
60
61
+ func (e * ErrorRegistry ) RegisterDefaultHandler (callback func (ctx context.Context , err error ) (int , any )) {
62
+ e .DefaultHandler = callback
63
+ }
64
+
65
+ func (e * ErrorRegistry ) defaultResponse (ctx context.Context , err error ) (int , any ) {
66
+ // In production, we should return a generic error message. If you want to know why, read this:
67
+ // https://owasp.org/www-community/Improper_Error_Handling
68
+ if e .DefaultHandler != nil {
69
+ return e .DefaultHandler (ctx , err )
70
+ }
71
+
72
+ return e .DefaultCode , e .DefaultResponse
73
+ }
74
+
75
75
// NewErrorResponse Returns an error response using the DefaultErrorRegistry. If no specific handler could be found,
76
- // it will return the defaults. It returns an HTTP status code and a response object.
77
- //
78
- // Deprecated: Please use v2 of this library
79
- //
80
- //nolint:gocritic // Unnamed return arguments are described
81
- func NewErrorResponse (err error ) (int , any ) {
82
- return NewErrorResponseFrom (DefaultErrorRegistry , err )
76
+ // it will return the defaults.
77
+ func NewErrorResponse (ctx context.Context , err error ) (int , any ) {
78
+ return NewErrorResponseFrom (DefaultErrorRegistry , ctx , err )
83
79
}
84
80
85
81
// NewErrorResponseFrom Returns an error response using the given registry. If no specific handler could be found,
86
- // it will return the defaults. It returns an HTTP status code and a response object.
87
- //
88
- // Deprecated: Please use v2 of this library
89
- //
90
- //nolint:gocritic // Unnamed return arguments are described
91
- func NewErrorResponseFrom (registry * ErrorRegistry , err error ) (int , any ) {
92
- errorType := fmt .Sprintf ("%T" , err )
82
+ // it will return the defaults.
83
+ func NewErrorResponseFrom [E error ](registry * ErrorRegistry , ctx context.Context , err E ) (int , any ) {
84
+ errorType := getErrorType [E ](err )
93
85
94
86
// If a handler is registered for the error type, use it.
95
87
if entry , ok := registry .handlers [errorType ]; ok {
96
- return entry (err )
88
+ return entry (ctx , err )
97
89
}
98
90
99
- // In production, we should return a generic error message. If you want to know why, read this:
100
- // https://owasp.org/www-community/Improper_Error_Handling
101
- return registry .DefaultCode , registry .DefaultResponse
91
+ return registry .defaultResponse (ctx , err )
102
92
}
103
93
104
94
// RegisterErrorHandler registers an error handler in DefaultErrorRegistry. The R type is the type of the response body.
105
- // Deprecated: Please use v2 of this library
106
- func RegisterErrorHandler [E error , R any ](handler ErrorHandler [E , R ]) {
95
+ func RegisterErrorHandler [E error ](handler func (context.Context , E ) (int , any )) {
107
96
RegisterErrorHandlerOn (DefaultErrorRegistry , handler )
108
97
}
109
98
110
99
// RegisterErrorHandlerOn registers an error handler in the given registry. The R type is the type of the response body.
111
- // Deprecated: Please use v2 of this library
112
- func RegisterErrorHandlerOn [E error , R any ](registry * ErrorRegistry , handler ErrorHandler [E , R ]) {
100
+ func RegisterErrorHandlerOn [E error ](registry * ErrorRegistry , handler func (context.Context , E ) (int , any )) {
113
101
// Name of the type
114
- errorType := fmt . Sprintf ( "%T" , * new (E ))
102
+ errorType := getErrorType [ E ]( new (E ))
115
103
116
104
// Wrap it in a closure, we can't save it directly because err E is not available in NewErrorResponseFrom. It will
117
105
// be available in the closure when it is called. Check out TestErrorResponseFrom_ReturnsErrorBInInterface for an example.
118
- registry .handlers [errorType ] = func (err error ) (int , any ) {
119
- // We can safely cast it here, because we know it's the right type.
120
- //nolint:errorlint // Not relevant, we're casting anyway
121
- return handler (err .(E ))
106
+ registry .handlers [errorType ] = func (ctx context.Context , err error ) (int , any ) {
107
+ return handler (ctx , err .(E ))
122
108
}
123
109
}
124
110
125
111
// RegisterCustomErrorTypeHandler registers an error handler in DefaultErrorRegistry. Same as RegisterErrorHandler,
126
112
// but you can set the fmt.Sprint("%T", err) error yourself. Allows you to register error types that aren't exported
127
113
// from their respective packages such as the uuid error or *errors.errorString. The R type is the type of the response body.
128
- // Deprecated: Please use v2 of this library
129
- func RegisterCustomErrorTypeHandler [R any ](errorType string , handler CustomErrorHandler [R ]) {
114
+ func RegisterCustomErrorTypeHandler (errorType string , handler func (ctx context.Context , err error ) (int , any )) {
130
115
RegisterCustomErrorTypeHandlerOn (DefaultErrorRegistry , errorType , handler )
131
116
}
132
117
133
118
// RegisterCustomErrorTypeHandlerOn registers an error handler in the given registry. Same as RegisterErrorHandlerOn,
134
119
// but you can set the fmt.Sprint("%T", err) error yourself. Allows you to register error types that aren't exported
135
120
// from their respective packages such as the uuid error or *errors.errorString. The R type is the type of the response body.
136
- // Deprecated: Please use v2 of this library
137
- func RegisterCustomErrorTypeHandlerOn [R any ](registry * ErrorRegistry , errorType string , handler CustomErrorHandler [R ]) {
121
+ func RegisterCustomErrorTypeHandlerOn (registry * ErrorRegistry , errorType string , handler func (ctx context.Context , err error ) (int , any )) {
138
122
// Wrap it in a closure, we can't save it directly
139
- registry .handlers [errorType ] = func (err error ) (int , any ) {
140
- return handler (err )
141
- }
123
+ registry .handlers [errorType ] = handler
142
124
}
143
125
144
126
// RegisterStringErrorHandler allows you to register an error handler for a simple errorString created with
145
127
// errors.New() or fmt.Errorf(). Can be used in case you are dealing with libraries that don't have exported
146
128
// error objects. Uses the DefaultErrorRegistry. The R type is the type of the response body.
147
- // Deprecated: Please use v2 of this library
148
- func RegisterStringErrorHandler [R any ](errorString string , handler ErrorStringHandler [R ]) {
129
+ func RegisterStringErrorHandler (errorString string , handler func (ctx context.Context , err string ) (int , any )) {
149
130
RegisterStringErrorHandlerOn (DefaultErrorRegistry , errorString , handler )
150
131
}
151
132
152
133
// RegisterStringErrorHandlerOn allows you to register an error handler for a simple errorString created with
153
134
// errors.New() or fmt.Errorf(). Can be used in case you are dealing with libraries that don't have exported
154
135
// error objects. The R type is the type of the response body.
155
- // Deprecated: Please use v2 of this library
156
- func RegisterStringErrorHandlerOn [R any ](registry * ErrorRegistry , errorString string , handler ErrorStringHandler [R ]) {
157
- registry .stringHandlers [errorString ] = func (err string ) (int , any ) {
158
- return handler (err )
136
+ func RegisterStringErrorHandlerOn (registry * ErrorRegistry , errorString string , handler func (ctx context.Context , err string ) (int , any )) {
137
+ registry .stringHandlers [errorString ] = handler
138
+ }
139
+
140
+ // getErrorType returns the errorType from the generic type. If the generic type returns the typealias "error",
141
+ // e.g. due to `type SomeError error`, retry with the concrete `err` value.
142
+ func getErrorType [E error ](err any ) string {
143
+ typeOf := reflect .ValueOf (new (E )).Type ()
144
+ for typeOf .Kind () == reflect .Pointer {
145
+ typeOf = typeOf .Elem ()
159
146
}
147
+ errorType := typeOf .String ()
148
+
149
+ if errorType == "error" {
150
+ // try once more but with err instead of new(E)
151
+ typeOf = reflect .ValueOf (err ).Type ()
152
+ for typeOf .Kind () == reflect .Pointer {
153
+ typeOf = typeOf .Elem ()
154
+ }
155
+ errorType = typeOf .String ()
156
+ }
157
+
158
+ return errorType
160
159
}
0 commit comments