-
Notifications
You must be signed in to change notification settings - Fork 6
/
errors.go
229 lines (196 loc) · 6.38 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
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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
package page
import (
"bytes"
"context"
"errors"
"fmt"
"runtime"
"time"
)
const MaxStackDepth = 50
// The ErrorPageTemplate type specifies the signature for the error template function. You can replace the built-in error
// function with your own function by setting the config.ErrorPage value. html is the html that was able to be generated before
// the error occurred, which can be helpful in tracking down the source of an error.
type ErrorPageFuncType func(ctx context.Context, html string, err *Error, buf *bytes.Buffer)
var ErrorPageFunc ErrorPageFuncType
// TODO: move to its own package so it is accessible from the entire framework
// The error structure, specifically designed to manage panics during request handling
type Error struct {
// the error string
Err error
// the time the error occurred
Time time.Time
// the copied context when the error occurred
Ctx *Context
// unwound Stack info
Stack []StackFrame
}
// StackFrame holds the file, line and function name in a call chain
type StackFrame struct {
File string
Line int
Func string
}
// DbError represents a database error.
type DbError struct {
Error
// DbStatement is the captured database statement if one caused the error, returned by the db adapter
DbStatement string
}
// NoErr represents no error. A request starts with this.
type NoErr struct {
}
// Known application specific errors
const (
FrameworkErrNone = iota
// FrameworkErrNoTemplate indicates a template does not exist. The control will move on to other ways of rendering.
// No message should be displayed.
FrameworkErrNoTemplate
// FrameworkErrRecordNotFound is a rare situation that might come up as a race condition error between viewing a
// record, and actually editing it. If in the time between clicking on a record to see detail, and viewing the detail,
// the record was deleted by another user, we would return this error.
// In a REST environment, this is 404 error
FrameworkErrRecordNotFound
// FrameworkErrNotAuthenticated indicates that the user needs to log in, but has not done so.
// This will result in a 401 http error.
FrameworkErrNotAuthenticated
// FrameworkErrNotAuthorized indicates the logged in user does not have permission to access the resource.
// This will result in a 403 error.
FrameworkErrNotAuthorized
// FrameworkErrRedirect indicates the resource is at a different location. This will result in a 303 error
// telling the browser to go that resource.
FrameworkErrRedirect
// FrameworkErrBadRequest is a generic bad request error, but most often it indicates that some FORM or GET
// parameter was expected, but was either missing or invalid. This is a 400 error.
FrameworkErrBadRequest
// FrameworkErrMethodNotAllowed is for REST clients to indicate that the user tried to use a method (i.e. GET or PUT)
// that is not allowed at the given endpoint. The HTTP spec says the client should also respond with an Allow
// header with a comma separated list of what methods are allowed. This is a 405 error.
FrameworkErrMethodNotAllowed
)
// FrameworkError is an expected error that is part of the framework. Usually you would respond to the error
// by displaying a message to the user, but not always.
type FrameworkError struct {
Err int
Location string
Message string // optional message
}
// NewFrameworkError creates a new FrameworkError
func NewFrameworkError(err int) FrameworkError {
return FrameworkError{Err: err}
}
func NewRedirectError(location string) FrameworkError {
return FrameworkError{Err: FrameworkErrRedirect, Location: location}
}
func (e FrameworkError) SetMessage(msg string) FrameworkError {
e.Message = msg
return e
}
// Error returns the error string
func (e FrameworkError) Error() string {
if e.Message != "" {
return e.Message
}
switch e.Err {
case FrameworkErrNoTemplate:
return "FormBase or control does not have a template" // just detected, this is not likely to be used
case FrameworkErrRecordNotFound:
return "Record does not exist."
case FrameworkErrNotAuthenticated:
return "You must log in."
case FrameworkErrNotAuthorized:
return "You are not authorized to access this information."
case FrameworkErrRedirect:
return "Redirecting to " + e.Location
case FrameworkErrBadRequest:
return "Your request did not make sense."
case FrameworkErrMethodNotAllowed:
return "The method of your request is not allowed. See the returned Allow header to know what is expected."
}
return ""
}
// HttpError returns the corresponding http error
func (e FrameworkError) HttpError() int {
switch e.Err {
case FrameworkErrNotAuthenticated: return 401
case FrameworkErrNotAuthorized: return 403
case FrameworkErrRecordNotFound: return 404
case FrameworkErrBadRequest: return 400
case FrameworkErrMethodNotAllowed: return 405
}
return 500
}
func RedirectHtml(loc string) string {
return fmt.Sprintf(`<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Refresh" content="0; url=%s" />
</head>
<body>
<p>Redirecting to <a href="%[1]s">%[1]s</a>.</p>
</body>
</html>`, loc)
}
func (e *NoErr) Error() string {
return ""
}
func IsError(e error) bool {
_, ok := e.(*NoErr)
return !ok
}
// Called by the page manager to record a system error
func newRunError(ctx context.Context, msg interface{}) *Error {
e := &Error{}
switch m := msg.(type) {
case string: // we panic'd
e.Err = errors.New(m)
e.fillErr(ctx, 2)
case error: // system generated error
e.Err = m
e.fillErr(ctx, 2)
default:
e.Err = fmt.Errorf("Error of type %T: %v", msg, msg)
e.fillErr(ctx, 1)
}
return e
}
// NewError return a generic message error
func NewError(ctx context.Context, msg string) *Error {
e := &Error{}
e.Err = errors.New(msg)
e.fillErr(ctx, 1)
return e
}
func (e *Error) fillErr(ctx context.Context, skip int) {
e.Time = time.Now()
e.Ctx = GetContext(ctx)
for i := 2 + skip; i < MaxStackDepth; i++ {
pc, file, line, ok := runtime.Caller(i)
if !ok {
break
}
name := ""
if f := runtime.FuncForPC(pc); f != nil {
name = f.Name()
}
frame := StackFrame{file, line, name}
e.Stack = append(e.Stack, frame)
}
}
func (e *Error) Error() string {
return e.Err.Error()
}
// NewDbErr returns a new database error
func NewDbErr(ctx context.Context, msg interface{}, dbStatement string) *DbError {
e := &DbError{}
switch m := msg.(type) {
case string:
e.Err = errors.New(m)
case error:
e.Err = m
default:
e.Err = fmt.Errorf("Error of type %T: %v", msg, msg)
}
e.fillErr(ctx, 1)
return e
}