-
Notifications
You must be signed in to change notification settings - Fork 40
/
Copy pathresponse.go
194 lines (160 loc) · 4.31 KB
/
response.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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
package neo
import (
"encoding/json"
"encoding/xml"
"net/http"
"os"
"path/filepath"
)
// Server response representation.
type Response struct {
Status int
writer http.ResponseWriter
request *http.Request
Cookie Cookie
data []byte
// defer file sending
file string
redirect string
_skipFlush bool
}
// making response representation
func makeResponse(request *http.Request, w http.ResponseWriter) *Response {
response := &Response{
request: request,
writer: w,
Cookie: Cookie{},
}
return response
}
///////////////////////////////////////////////////////////////////
// Creating Responses
///////////////////////////////////////////////////////////////////
// Will produce JSON string representation of passed object,
// and send it to client
func (r *Response) Json(obj interface{}) error {
res, err := json.Marshal(obj)
if err != nil {
return err
}
r.writer.Header().Set("Content-Type", "application/json")
return r.Raw(res)
}
// Will produce XML string representation of passed object,
// and send it to client
func (r *Response) Xml(obj interface{}) error {
res, err := xml.Marshal(obj)
if err != nil {
return err
}
r.writer.Header().Set("Content-Type", "application/xml")
return r.Raw(res)
}
// Will send provided Text to client.
func (r *Response) Text(text string) error {
return r.Raw([]byte(text))
}
// Will look for template, render it, and send rendered HTML to client.
// Second argument is data which will be passed to client.
func (r *Response) Tpl(name string, data interface{}) error {
log.Infof("Rendering template %s", name)
err := renderTpl(r.writer, name, data)
if err != nil {
r.Status = http.StatusNotFound
return err
}
r.SkipFlush()
return nil
}
// Send Raw data to client.
func (r *Response) Raw(data []byte) error {
r.data = data
return nil
}
// Redirect to url with status
func (r *Response) Redirect(url string) error {
r.redirect = url
return nil
}
// Write raw response. Implements ResponseWriter.Write.
func (r *Response) Write(b []byte) (int, error) {
return r.writer.Write(b)
}
// Get Header. Implements ResponseWriter.Header.
func (r *Response) Header() http.Header {
return r.writer.Header()
}
// Write Header. Implements ResponseWriter.WriterHeader.
func (r *Response) WriteHeader(s int) {
r.writer.WriteHeader(s)
}
// Get http.ResponseWriter directly
func (r *Response) Writer() http.ResponseWriter {
return r.writer
}
// Checking if file exist.
// todo: consider moving this to utils.go
func (r *Response) fileExists(file string) bool {
if _, err := os.Stat(file); err == nil {
return true
}
r.Status = http.StatusNotFound
log.Warnf("cannot find %s file", file)
return false
}
// Find file, and send it to client.
func (r *Response) File(path string) error {
abspath, err := filepath.Abs(path)
if err != nil {
return err
}
if !r.fileExists(abspath) {
return err
}
r.file = abspath
return nil
}
// Serving static file.
func (r *Response) serveFile(file string) error {
log.Debugf("serving file %s", file)
http.ServeFile(r.writer, r.request, file)
return nil
}
// Will be called from ``flush`` Response method if user called ``File`` method.
func (r *Response) sendFile() {
log.Debugf("sending file %s", r.file)
base := filepath.Base(r.file)
r.writer.Header().Set("Content-Disposition", "attachment; filename="+base)
http.ServeFile(r.writer, r.request, r.file)
}
///////////////////////////////////////////////////////////////////
// Writing Response
///////////////////////////////////////////////////////////////////
// If it is called, Neo will skip calling ResponseWriter's write method.
// Usecase for this is when we render HTML template for example, because Neo uses Go html/template for
// writing to ResponseWriter.
func (r *Response) SkipFlush() {
r._skipFlush = true
}
// Write result to ResponseWriter.
func (r *Response) flush() {
if r._skipFlush {
log.Debug("Already sent. Skipping flushing")
return
}
// set all cookies to response object
for k, v := range r.Cookie {
log.Debug(k)
http.SetCookie(r.writer, v)
}
// in case of file call separate function for piping file to client
if len(r.file) > 0 {
log.Debugf("found file, sending")
r.sendFile()
} else if len(r.redirect) > 0 {
http.Redirect(r.writer, r.request, r.redirect, r.Status)
} else {
r.writer.WriteHeader(r.Status)
r.writer.Write(r.data)
}
}