forked from Cepave/open-falcon-backend
/
error_handler.go
188 lines (172 loc) · 4.9 KB
/
error_handler.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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
package gin
import (
"fmt"
or "github.com/Cepave/open-falcon-backend/common/runtime"
json "github.com/bitly/go-simplejson"
"github.com/gin-gonic/gin"
"net/http"
)
// Defines the error used to represent the "409 Conflict" error
//
// Usage
//
// Case 1: The conflict of unique key on a new or modified data
//
// Case 2: The conflict of complex logical of business on modified data
//
// HTTP Specification
//
// Following paragraph comes from RFC-2616(HTTP/1.1)
//
// The request could not be completed due to a conflict with the current state of the resource.
// This code is only allowed in situations where it is expected that
// the user might be able to resolve the conflict and resubmit the request.
//
// The response body SHOULD include enough information for the user to
// recognize the source of the conflict.
// Ideally, the response entity would include enough information for the user or
// user agent to fix the problem; however,
// that might not be possible and is not required.
//
// Conflicts are most likely to occur in response to a PUT request.
// For example, if versioning were being used and the entity being PUT included changes to
// a resource which conflict with those made by an earlier (third-party) request,
// the server might use the 409 response to indicate that it can't complete the request.
//
// In this case, the response entity would likely contain a list of
// the differences between the two versions in a format defined by the response Content-Type.
//
// See: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
type DataConflictError struct {
ErrorCode int32
ErrorMessage string
}
// Implements the error interface
func (e DataConflictError) Error() string {
return fmt.Sprintf("[%d] %s", e.ErrorCode, e.ErrorMessage)
}
// Marshal this type of error to:
//
// {
// "http_status": 409,
// "error_code": e.ErrorCode,
// "error_message": e.ErrorMessage,
// }
func (e DataConflictError) MarshalJSON() ([]byte, error) {
jsonObject := json.New()
jsonObject.Set("http_status", http.StatusConflict)
jsonObject.Set("error_code", e.ErrorCode)
jsonObject.Set("error_message", e.ErrorMessage)
return jsonObject.MarshalJSON()
}
// This callback function is used to process panic object
type PanicProcessor func(c *gin.Context, panic interface{})
// Builds a gin.HandlerFunc, which is used to handle not-nil object of panic
func BuildJsonPanicProcessor(panicProcessor PanicProcessor) gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
p := recover()
if p == nil {
return
}
panicProcessor(c, p)
}()
c.Next()
}
}
// Process various of panic object with corresponding HTTP status
//
// ValidationError - Use http.StatusBadRequest as output status
//
// BindJsonError - Use http.StatusBadRequest as output status
//
// Otherwise, use http.StatusInternalServerError as output status
func DefaultPanicProcessor(c *gin.Context, panicObject interface{}) {
stack := or.GetCallerInfoStack(2, 16).AsStringStack()
logger.Warnf("[GIN] Got panic: %v", panicObject)
for _, messageInStack := range stack {
logger.Warnf("%s", messageInStack)
}
switch errObject := panicObject.(type) {
case ValidationError:
c.JSON(
http.StatusBadRequest,
map[string]interface{}{
"http_status": http.StatusBadRequest,
"error_code": -1,
"error_message": errObject.Error(),
},
)
case BindJsonError:
c.JSON(
http.StatusBadRequest,
map[string]interface{}{
"http_status": http.StatusBadRequest,
"error_code": -101,
"error_message": errObject.Error(),
},
)
case DataConflictError:
c.JSON(
http.StatusConflict,
map[string]interface{}{
"http_status": http.StatusConflict,
"error_code": errObject.ErrorCode,
"error_message": errObject.ErrorMessage,
},
)
default:
c.JSON(
http.StatusInternalServerError,
map[string]interface{}{
"http_status": http.StatusInternalServerError,
"error_code": -1,
"error_message": fmt.Sprintf("%v", panicObject),
"error_stack": stack,
},
)
}
}
// Output http.StatusConflict as JSON.
func JsonConflictHandler(c *gin.Context, body interface{}) {
c.JSON(
http.StatusConflict,
body,
)
}
// Output http.StatusMethodNotAllowed as JSON;
//
// {
// "http_status": 405,
// "error_code": -1,
// "method": c.Request.Method,
// "uri": c.Request.RequestURI,
// }
func JsonNoMethodHandler(c *gin.Context) {
c.JSON(
http.StatusNotFound,
map[string]interface{}{
"http_status": http.StatusMethodNotAllowed,
"error_code": -1,
"method": c.Request.Method,
"uri": c.Request.RequestURI,
},
)
}
// Output http.StatusNotFound as JSON;
//
// {
// "http_status": 404,
// "error_code": -1,
// "uri": c.Request.RequestURI,
// }
func JsonNoRouteHandler(c *gin.Context) {
c.JSON(
http.StatusNotFound,
map[string]interface{}{
"http_status": http.StatusNotFound,
"error_code": -1,
"uri": c.Request.RequestURI,
},
)
}