-
Notifications
You must be signed in to change notification settings - Fork 1
/
error.go
147 lines (121 loc) · 3.23 KB
/
error.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
// Copyright 2016 Tim Shannon. All rights reserved.
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.
package main
import (
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
"github.com/timshannon/ironsmith/datastore"
)
const (
acceptHTML = "text/html"
)
// Err404 is a standard 404 error response
var Err404 = errors.New("Resource not found")
func errHandled(err error, w http.ResponseWriter, r *http.Request) bool {
if err == nil {
return false
}
if err == datastore.ErrNotFound {
four04(w, r)
return true
}
var status, errMsg string
errMsg = err.Error()
switch err.(type) {
case *Fail:
status = statusFail
case *http.ProtocolError, *json.SyntaxError, *json.UnmarshalTypeError:
//Hardcoded external errors which can bubble up to the end users
// without exposing internal server information, make them failures
err = FailFromErr(err)
status = statusFail
errMsg = fmt.Sprintf("We had trouble parsing your input, please check your input and try again: %s", err)
default:
status = statusError
log.Printf("An error has occurred from a web request: %s", errMsg)
errMsg = "An internal server error has occurred"
}
if status == statusFail {
respondJsendCode(w, &JSend{
Status: status,
Message: errMsg,
Data: err.(*Fail).Data,
}, err.(*Fail).HTTPStatus)
} else {
respondJsend(w, &JSend{
Status: status,
Message: errMsg,
})
}
return true
}
// four04 is a standard 404 response if request header accepts text/html
// they'll get a 404 page, otherwise a json response
func four04(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Content-Type", "application/json")
response := &JSend{
Status: statusFail,
Message: "Resource not found",
Data: r.URL.String(),
}
w.WriteHeader(http.StatusNotFound)
result, err := json.Marshal(response)
if err != nil {
log.Printf("Error marshalling 404 response: %s", err)
return
}
_, err = w.Write(result)
if err != nil {
log.Printf("Error in four04: %s", err)
}
}
// Fail is an error whose contents can be exposed to the client and is usually the result
// of incorrect client input
type Fail struct {
Message string `json:"message,omitempty"`
Data interface{} `json:"data,omitempty"`
HTTPStatus int `json:"-"` //gets set in the error response
}
func (f *Fail) Error() string {
return f.Message
}
// NewFail creates a new failure, data is optional
func NewFail(message string, data ...interface{}) error {
return &Fail{
Message: message,
Data: data,
HTTPStatus: 0,
}
}
// FailFromErr returns a new failure based on the passed in error, data is optional
// if passed in error is nil, then nil is returned
func FailFromErr(err error, data ...interface{}) error {
if err == nil {
return nil
}
return NewFail(err.Error(), data...)
}
// IsEqual tests whether an error is equal to another error / failure
func (f *Fail) IsEqual(err error) bool {
if err == nil {
return false
}
return err.Error() == f.Error()
}
// IsFail tests whether the passed in error is a failure
func IsFail(err error) bool {
if err == nil {
return false
}
switch err.(type) {
case *Fail:
return true
default:
return false
}
}