-
Notifications
You must be signed in to change notification settings - Fork 31
/
responses.go
166 lines (138 loc) · 4.86 KB
/
responses.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
package webgo
import (
"encoding/json"
"fmt"
"html/template"
"net/http"
)
var (
jsonErrPayload = []byte{}
)
// ErrorData used to render the error page
type ErrorData struct {
ErrCode int
ErrDescription string
}
// dOutput is the standard/valid output wrapped in `{data: <payload>, status: <http response status>}`
type dOutput struct {
Data interface{} `json:"data"`
Status int `json:"status"`
}
// errOutput is the error output wrapped in `{errors:<errors>, status: <http response status>}`
type errOutput struct {
Errors interface{} `json:"errors"`
Status int `json:"status"`
}
const (
// HeaderContentType is the key for mentioning the response header content type
HeaderContentType = "Content-Type"
// JSONContentType is the MIME type when the response is JSON
JSONContentType = "application/json"
// HTMLContentType is the MIME type when the response is HTML
HTMLContentType = "text/html; charset=UTF-8"
// ErrInternalServer to send when there's an internal server error
ErrInternalServer = "Internal server error"
)
// SendHeader is used to send only a response header, i.e no response body
func SendHeader(w http.ResponseWriter, rCode int) {
w.WriteHeader(rCode)
}
func crwAsserter(w http.ResponseWriter, rCode int) http.ResponseWriter {
if crw, ok := w.(*customResponseWriter); ok {
crw.statusCode = rCode
return crw
}
return newCRW(w, rCode)
}
// Send sends a completely custom response without wrapping in the
// `{data: <data>, status: <int>` struct
func Send(w http.ResponseWriter, contentType string, data interface{}, rCode int) {
w = crwAsserter(w, rCode)
w.Header().Set(HeaderContentType, contentType)
_, err := fmt.Fprint(w, data)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(ErrInternalServer))
LOGHANDLER.Error(err)
}
}
// SendResponse is used to respond to any request (JSON response) based on the code, data etc.
func SendResponse(w http.ResponseWriter, data interface{}, rCode int) {
w = crwAsserter(w, rCode)
w.Header().Add(HeaderContentType, JSONContentType)
err := json.NewEncoder(w).Encode(dOutput{Data: data, Status: rCode})
if err == nil {
return
}
// assuming the error was related to JSON encoding, so reattempting to respond
// with a static payload. This could still fail in case of network write or other error(s)
w = crwAsserter(w, http.StatusInternalServerError)
_, _ = w.Write(jsonErrPayload)
LOGHANDLER.Error(err)
}
// SendError is used to respond to any request with an error
func SendError(w http.ResponseWriter, data interface{}, rCode int) {
w = crwAsserter(w, rCode)
w.Header().Add(HeaderContentType, JSONContentType)
err := json.NewEncoder(w).Encode(errOutput{data, rCode})
if err == nil {
return
}
// assuming the error was related to JSON encoding, so reattempting to respond
// with a static payload. This could still fail in case of network write or other error(s)
w = crwAsserter(w, http.StatusInternalServerError)
_, _ = w.Write(jsonErrPayload)
LOGHANDLER.Error(err)
}
// Render is used for rendering templates (HTML)
func Render(w http.ResponseWriter, data interface{}, rCode int, tpl *template.Template) {
w = crwAsserter(w, rCode)
// In case of HTML response, setting appropriate header type for text/HTML response
w.Header().Set(HeaderContentType, HTMLContentType)
// Rendering an HTML template with appropriate data
err := tpl.Execute(w, data)
if err != nil {
Send(w, "text/plain", ErrInternalServer, http.StatusInternalServerError)
LOGHANDLER.Error(err.Error())
}
}
// R200 - Successful/OK response
func R200(w http.ResponseWriter, data interface{}) {
SendResponse(w, data, http.StatusOK)
}
// R201 - New item created
func R201(w http.ResponseWriter, data interface{}) {
SendResponse(w, data, http.StatusCreated)
}
// R204 - empty, no content
func R204(w http.ResponseWriter) {
SendHeader(w, http.StatusNoContent)
}
// R302 - Temporary redirect
func R302(w http.ResponseWriter, data interface{}) {
SendResponse(w, data, http.StatusFound)
}
// R400 - Invalid request, any incorrect/erraneous value in the request body
func R400(w http.ResponseWriter, data interface{}) {
SendError(w, data, http.StatusBadRequest)
}
// R403 - Unauthorized access
func R403(w http.ResponseWriter, data interface{}) {
SendError(w, data, http.StatusForbidden)
}
// R404 - Resource not found
func R404(w http.ResponseWriter, data interface{}) {
SendError(w, data, http.StatusNotFound)
}
// R406 - Unacceptable header. For any error related to values set in header
func R406(w http.ResponseWriter, data interface{}) {
SendError(w, data, http.StatusNotAcceptable)
}
// R451 - Resource taken down because of a legal request
func R451(w http.ResponseWriter, data interface{}) {
SendError(w, data, http.StatusUnavailableForLegalReasons)
}
// R500 - Internal server error
func R500(w http.ResponseWriter, data interface{}) {
SendError(w, data, http.StatusInternalServerError)
}