-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
httperr.go
116 lines (98 loc) · 2.96 KB
/
httperr.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
// Package httperr provides a middleware to make it easier to handle http
// errors in a common way.
package httperr
import (
"encoding/json"
"errors"
"fmt"
"net/http"
)
// Error is a HTTP error with an underlying error and a status code.
type Error struct {
Err error
Status int
}
func (e Error) Error() string {
return e.Err.Error()
}
// Is conforms with errors.Is.
func (e Error) Is(err error) bool {
switch err.(type) {
case Error:
return true
default:
return errors.Is(e.Err, err)
}
}
// Wrap a given error with the given status.
// Returns nil if the given error is nil.
func Wrap(err error, status int) error {
if err == nil {
return nil
}
return Error{ // nolint: wsl
Err: err,
Status: status,
}
}
// Errorf creates a new error and wraps it with the given status
func Errorf(status int, format string, args ...interface{}) error {
return Wrap(fmt.Errorf(format, args...), status)
}
// Handler is like http.Handler, but the ServeHTTP method can also return
// an error.
type Handler interface {
ServeHTTP(w http.ResponseWriter, r *http.Request) error
}
// HandlerFunc is just like http.HandlerFunc.
type HandlerFunc func(http.ResponseWriter, *http.Request) error
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) error {
return f(w, r)
}
// ErrorHandler handles an error.
type ErrorHandler func(w http.ResponseWriter, r *http.Request, err error, status int)
// DefaultErrorHandler is the default error handler.
// It converts the error to JSON and prints writes it to the response.
func DefaultErrorHandler(w http.ResponseWriter, r *http.Request, err error, status int) {
msg := err.Error()
bts, _ := json.Marshal(errorResponse{
Error: msg,
})
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.WriteHeader(status)
fmt.Fprintln(w, string(bts))
}
// NewWithHandler() wraps a given http.Handler and returns a http.Handler.
// You can also customize how the error is handled.
func NewWithHandler(next Handler, eh ErrorHandler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
err := next.ServeHTTP(w, r)
if err == nil {
return
}
herr := Error{}
if errors.As(err, &herr) {
eh(w, r, herr, herr.Status)
} else {
eh(w, r, err, http.StatusInternalServerError)
}
})
}
// New wraps a given http.Handler and returns a http.Handler.
func New(next Handler) http.Handler {
return NewWithHandler(next, DefaultErrorHandler)
}
// NewFWithHandler wraps a given http.HandlerFunc and return a http.Handler.
// You can also customize how the error is handled.
func NewFWithHandler(next HandlerFunc, eh ErrorHandler) http.Handler { // nolint: interfacer
return NewWithHandler(next, eh)
}
// NewF wraps a given http.HandlerFunc and return a http.Handler.
func NewF(next HandlerFunc) http.Handler { // nolint: interfacer
return New(next)
}
type errorResponse struct {
Error string `json:"error"`
}