This repository has been archived by the owner on May 6, 2023. It is now read-only.
/
context.go
316 lines (255 loc) 路 8.29 KB
/
context.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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
package dolphin
import (
"encoding/json"
"mime/multipart"
"net/http"
"sync"
)
// Context is the context instance for the request.
type Context struct {
// Request is the wrapped HTTP request.
Request *Request
// Response is the wrapped HTTP response.
Response *Response
// app is the framework application instance.
app *App
// handlers is the handler chain.
handlers HandlerChain
// index is the current handler index.
index int
// isAbort is the flag to indicate if the current handler chain should be
// aborted.
isAbort bool
// sm is the mutex for the context state.
sm sync.Mutex
// state is the context state, it can be used to store any data and pass to
// the next handlers.
state map[string]interface{}
}
// allocateContext returns a new context instance.
func allocateContext() *Context {
return &Context{}
}
// reset the context instance to initial state.
func (ctx *Context) reset(app *App, req *http.Request) {
ctx.Request = app.reqPool.Get().(*Request)
ctx.Request.reset()
ctx.Request.request = req
ctx.Response = app.resPool.Get().(*Response)
ctx.Response.reset()
ctx.app = app
ctx.handlers = HandlerChain{}
ctx.index = -1
ctx.isAbort = false
ctx.state = map[string]interface{}{}
ctx.Use(app.handlers...)
}
// finalize releases the context, request, and response resources.
func (ctx *Context) finalize() {
ctx.app.reqPool.Put(ctx.Request)
ctx.app.resPool.Put(ctx.Response)
ctx.app.pool.Put(ctx)
}
// writeResponse writes data from context to the response.
func (ctx *Context) writeResponse(rw http.ResponseWriter) {
ctx.Response.write(rw)
}
// send writes the response data to the body buffer, sets the contentType and the status code
// if it's set.
func (ctx *Context) send(data []byte, contentType string, statusCode ...int) error {
if len(statusCode) >= 1 {
err := ctx.Response.SetStatusCode(statusCode[0])
if err != nil {
debugPrintf("Failed to set HTTP status code: %v", err)
return err
}
} else {
ctx.Response.SetStatusCode(http.StatusOK)
}
ctx.SetContentType(contentType)
_, err := ctx.Response.SetBody(data)
if err != nil {
debugPrintf("Failed to set response body: %v", err)
return err
}
return nil
}
// Abort stops the current handler chain.
func (ctx *Context) Abort() {
ctx.isAbort = true
}
// Next calls the next handler in the handler chain.
func (ctx *Context) Next() {
ctx.index++
for !ctx.isAbort && ctx.index < len(ctx.handlers) {
ctx.handlers[ctx.index](ctx)
ctx.index++
}
}
// Use registers one or more middlewares or request handlers to the context.
func (ctx *Context) Use(handlers ...HandlerFunc) {
ctx.handlers = append(ctx.handlers, handlers...)
}
// Log call the app logger with the given format and args.
func (ctx *Context) Log(fmt string, args ...interface{}) {
ctx.app.log(fmt, args...)
}
// Get retrieves the value of the given key from the context state.
func (ctx *Context) Get(key string) (interface{}, bool) {
ctx.sm.Lock()
defer ctx.sm.Unlock()
val, ok := ctx.state[key]
return val, ok
}
// Has returns true if the given key exists in the context state.
func (ctx *Context) Has(key string) bool {
ctx.sm.Lock()
defer ctx.sm.Unlock()
_, ok := ctx.state[key]
return ok
}
// Set sets the value of the given key to the context state.
func (ctx *Context) Set(key string, val interface{}) interface{} {
ctx.sm.Lock()
defer ctx.sm.Unlock()
oldVal := ctx.state[key]
ctx.state[key] = val
return oldVal
}
// Cookie returns the named cookie provided in the request.
func (ctx *Context) Cookie(key string) (*http.Cookie, error) {
return ctx.Request.Cookie(key)
}
// File returns the named file provided in the request.
func (ctx *Context) File(key string) (multipart.File, *multipart.FileHeader, error) {
return ctx.Request.File(key)
}
// Header returns the header value from the request by the given key.
func (ctx *Context) Header(key string) string {
return ctx.Request.Header(key)
}
// MultiValuesHeader returns a string array value for the given key from the request header.
func (ctx *Context) MultiValuesHeader(key string) []string {
return ctx.Request.MultiValuesHeader(key)
}
// IP returns the request client IP address.
func (ctx *Context) IP() string {
return ctx.Request.IP()
}
// Method returns the request method.
func (ctx *Context) Method() string {
return ctx.Request.Method()
}
// Path returns the request path.
func (ctx *Context) Path() string {
return ctx.Request.Path()
}
// Post returns the request post data.
func (ctx *Context) Post() string {
return ctx.Request.Post()
}
// PostJSON gets request body and parses to the given struct.
func (ctx *Context) PostJSON(payload interface{}) error {
body := ctx.Request.Post()
err := json.Unmarshal([]byte(body), payload)
if err != nil {
return err
}
return nil
}
// PostForm returns the request form data.
func (ctx *Context) PostForm(key string) string {
return ctx.Request.PostForm(key)
}
// Query returns the query value from the request by the given key.
func (ctx *Context) Query(key string) string {
return ctx.Request.Query(key)
}
// QueryDefault returns the query value from the request by the given key, and returns the
// default value if the key is not found.
func (ctx *Context) QueryDefault(key, defaultValue string) string {
val := ctx.Query(key)
if val == "" {
return defaultValue
}
return val
}
// MultiValuesQuery returns a string array value for the given key from the request query.
func (ctx *Context) MultiValuesQuery(key string) []string {
return ctx.Request.MultiValuesQuery(key)
}
// MultiValuesQueryDefault returns a string array value for the given key from the request query
// string, and returns the default values if the key is not exists.
func (ctx *Context) MultiValuesQueryDefault(key string, defaultValues []string) []string {
val := ctx.MultiValuesQuery(key)
if len(val) == 0 {
return defaultValues
}
return val
}
// Success writes the given data to the response body as JSON, and set the status code to 200 (OK).
func (ctx *Context) Success(data interface{}) error {
return ctx.JSON(data, http.StatusOK)
}
// Fail writes the given data to the response body as JSON, and set the status code
// to 400 (Bad Request).
func (ctx *Context) Fail(data interface{}) error {
return ctx.JSON(data, http.StatusBadRequest)
}
// JSON stringifies and writes the given data to the response body, and set the content type
// to "application/json".
func (ctx *Context) JSON(data interface{}, statusCode ...int) error {
payload, err := json.Marshal(data)
if err != nil {
return err
}
return ctx.send(payload, MIMETypeJSON, statusCode...)
}
// HTML writes the given data to the response body as HTML, and set the content type
// to "text/html".
func (ctx *Context) HTML(html string, statusCode ...int) error {
return ctx.send([]byte(html), MIMETypeHTML, statusCode...)
}
// String writes the given string to the response body, and sets the context type
// to "text/plain".
func (ctx *Context) String(data string, statusCode ...int) error {
return ctx.send([]byte(data), MIMETypeText, statusCode...)
}
// Redirect redirects the request to the given URL, it'll set status code to 302 (Found) as
// default status code if it isn't set.
func (ctx *Context) Redirect(url string, statusCode ...int) {
if url == "back" {
url = ctx.Request.Header(HeaderReferrer)
}
code := http.StatusFound
if len(statusCode) > 0 {
code = statusCode[0]
}
ctx.Response.SetStatusCode(code)
ctx.Response.SetHeader(HeaderLocation, url)
}
// Write writes the given data to the response body.
func (ctx *Context) Write(data []byte) (int, error) {
return ctx.Response.SetBody(data)
}
// AddCookies adds one or more given cookies to the response.
func (ctx *Context) AddCookies(cookies ...*http.Cookie) {
ctx.Response.AddCookies(cookies...)
}
// AddHeader appends the given header pair to the response.
func (ctx *Context) AddHeader(key, val string) {
ctx.Response.AddHeader(key, val)
}
// SetHeader sets the value of the given header key.
func (ctx *Context) SetHeader(key, val string) {
ctx.Response.SetHeader(key, val)
}
// SetStatusCode sets the status code of the response.
func (ctx *Context) SetStatusCode(code int) error {
return ctx.Response.SetStatusCode(code)
}
// SetContentType sets the response HTTP header "Content-Type" field to the
// specific MIME type value.
func (ctx *Context) SetContentType(contentType string) {
ctx.Response.SetHeader(HeaderContentType, contentType)
}