-
Notifications
You must be signed in to change notification settings - Fork 0
/
encoder.go
140 lines (114 loc) · 3.54 KB
/
encoder.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
package restencoder
import (
"encoding/json"
"log"
"net/http"
)
// ResponseConfig holds all settings to write a REST response
type ResponseConfig struct {
StatusCode int
Headers map[string]string
JSONBody any
}
// ResponseOption defines how to set up a response
type ResponseOption func(opts *ResponseConfig)
// StatusCode sets the response HTTP status code
func StatusCode(code int) ResponseOption {
return func(opts *ResponseConfig) {
opts.StatusCode = code
}
}
// Header sets a header entry in the HTTP response
func Header(key, value string) ResponseOption {
return func(opts *ResponseConfig) {
opts.Headers[key] = value
}
}
// JSONBody sets the HTTP response body to be serialized as json
func JSONBody(b any) ResponseOption {
return func(opts *ResponseConfig) {
opts.Headers["Content-Type"] = "application/json; charset=utf-8"
opts.JSONBody = b
}
}
// ErrorResponse is the contract used for failures.
type ErrorResponse struct {
Code string `json:"code,omitempty"`
ErrorMessage string `json:"error,omitempty"`
}
// Error sets the error message on the body using the error content.
// It changes the response body to ErrorResponse and default the status code
// to 500 (Internal Server Error) if no error status code is set.
func Error(err error) ResponseOption {
return func(opts *ResponseConfig) {
errRes, ok := opts.JSONBody.(ErrorResponse)
if !ok {
errRes = ErrorResponse{}
}
errRes.ErrorMessage = err.Error()
opts.JSONBody = errRes
opts.Headers["Content-Type"] = "application/json; charset=utf-8"
if opts.StatusCode >= 200 && opts.StatusCode <= 399 {
opts.StatusCode = http.StatusInternalServerError
}
}
}
// ErrorCode sets the error code on the body.
// It changes the response body to ErrorResponse and default the status code
// to 500 (Internal Server Error) if no error status code is set.
func ErrorCode(errCode string) ResponseOption {
return func(opts *ResponseConfig) {
errRes, ok := opts.JSONBody.(ErrorResponse)
if !ok {
errRes = ErrorResponse{}
}
errRes.Code = errCode
opts.JSONBody = errRes
opts.Headers["Content-Type"] = "application/json; charset=utf-8"
if opts.StatusCode >= 200 && opts.StatusCode <= 399 {
opts.StatusCode = http.StatusInternalServerError
}
}
}
// ErrorMessage sets the error message on the body with the given string.
// It changes the response body to ErrorResponse and default the status code
// to 500 (Internal Server Error) if no error status code is set.
func ErrorMessage(msg string) ResponseOption {
return func(opts *ResponseConfig) {
errRes, ok := opts.JSONBody.(ErrorResponse)
if !ok {
errRes = ErrorResponse{}
}
errRes.ErrorMessage = msg
opts.JSONBody = errRes
opts.Headers["Content-Type"] = "application/json; charset=utf-8"
if opts.StatusCode >= 200 && opts.StatusCode <= 399 {
opts.StatusCode = http.StatusInternalServerError
}
}
}
func newResponseConfig() ResponseConfig {
return ResponseConfig{
StatusCode: http.StatusOK,
Headers: make(map[string]string),
}
}
// Respond write the information back to the client.
func Respond(w http.ResponseWriter, opts ...ResponseOption) {
responseConfig := newResponseConfig()
for _, responseOption := range opts {
responseOption(&responseConfig)
}
w.WriteHeader(responseConfig.StatusCode)
for k, v := range responseConfig.Headers {
w.Header().Set(k, v)
}
if responseConfig.JSONBody == nil {
return
}
encoder := json.NewEncoder(w)
if err := encoder.Encode(&responseConfig.JSONBody); err != nil {
log.Printf("failed to encode response body: %s\n", err)
return
}
}