-
Notifications
You must be signed in to change notification settings - Fork 0
/
response_error.go
176 lines (154 loc) · 4.56 KB
/
response_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
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
package server
import (
"context"
"errors"
"fmt"
"net/http"
)
// Err creates a new ErrorBuilder with the provided error value.
// this is equivalent to calling
//
// Error().Err(err).
func Err(err error) *ErrorBuilder {
return Error().Err(err)
}
// Error creates a new ErrorBuilder with a default error value.
//
// Default ResponseError:
//
// {
// "error": "unknown error"
// "statusCode": 500
// "requestId": "..."
// }
//
// value 'requestId' is generated by requestIDFunc, which is set by SetRequestIDFunc
// if not set, it will be omitted from response
func Error() *ErrorBuilder {
return &ErrorBuilder{
err: errors.New("unknown error"),
msg: "",
status: http.StatusInternalServerError,
data: nil,
}
}
type ErrorBuilder struct {
err error
msg string
status int
data any
}
// Err sets the error value that will be embedded into the resulting ResponseError
// returned from a successful call to Write. The error.Err() value will be used
// as the message in the response if no message is provided.
func (b *ErrorBuilder) Err(err error) *ErrorBuilder {
b.err = err
return b
}
// Errf is the same as Err, but allows you to provide a format string and arguments
// that will be used to create a new error. This calls fmt.Errorf(format, args...)
// to create the error.
func (b *ErrorBuilder) Errf(format string, args ...any) *ErrorBuilder {
b.err = fmt.Errorf(format, args...)
return b
}
// Msg sets the message that will be used in the response. If no message is provided,
// the error.Err() value will be used as the message.
func (b *ErrorBuilder) Msg(msg string) *ErrorBuilder {
b.msg = msg
return b
}
// Msgf is the same as Msg, but allows you to provide a format string and arguments
// that will be used to create the message. This calls fmt.Sprintf(format, args...)
// to create the message.
func (b *ErrorBuilder) Msgf(format string, args ...any) *ErrorBuilder {
b.msg = fmt.Sprintf(format, args...)
return b
}
// Code sets the HTTP status code that will be used in the response. If no code is
// provided, http.StatusInternalServerError will be used.
//
// Deprecated: Use Status instead.
func (b *ErrorBuilder) Code(code int) *ErrorBuilder {
return b.Status(code)
}
// Status sets the HTTP status code that will be used in the response. If no code is
// provided, http.StatusInternalServerError will be used.
func (b *ErrorBuilder) Status(status int) *ErrorBuilder {
b.status = status
return b
}
// Data sets the data that will be included in the response. The data will be
// marshaled to JSON and included in the response body.
//
// Example JSON:
//
// {
// "error": "invalid request body"
// "statusCode": 400
// "data": {
// "foo": "bar"
// }
// }
func (b *ErrorBuilder) Data(data any) *ErrorBuilder {
b.data = data
return b
}
func (b *ErrorBuilder) responseMsg() string {
if b.msg == "" {
return b.err.Error()
}
return b.msg
}
// ErrorResp is the JSON response body for an error response. It contains the
// message, status code, request ID, and optional data.
type ErrorResp struct {
Message string `json:"message"`
StatusCode int `json:"statusCode"`
RequestID string `json:"requestId,omitempty"`
Data any `json:"data,omitempty"`
}
// Write sends an error response back to the client with properties in the
// ErrorBuilder. If the code is http.StatusNoContent, no body is sent. It returns
// a type of server.ResponseError that can be if you are using an error middleware
// that logs errors.
func (b *ErrorBuilder) Write(ctx context.Context, w http.ResponseWriter) error {
body := ErrorResp{
Message: b.responseMsg(),
StatusCode: b.status,
RequestID: requestIDFunc(ctx),
Data: b.data,
}
err := JSON(w, b.status, body)
if err != nil {
return err
}
return ResponseError{
cause: b.err,
msg: b.responseMsg(),
}
}
// ResponseError is an error that can be returned from a call to Write. It
// contains the underlying error and the optional message send to the client.
// Unwrap can be used to get the underlying error, and Error can be used to
// get the message sent to the client.
type ResponseError struct {
cause error
msg string
}
func (e ResponseError) Error() string {
return e.msg
}
func (e ResponseError) Unwrap() error {
return e.cause
}
// IsResponseError returns true if the error is a ResponseError or wraps a ResponseError. If
// the error is a ResponseError, the caller can assume that the HTTP status code and message
// have already been sent to the client.
func IsResponseError(err error) bool {
if err == nil {
return false
}
var e ResponseError
return errors.As(err, &e)
}