-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontext.go
195 lines (165 loc) · 5.53 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
// MIT License
//
// Copyright (c) 2017 José Santos <[email protected]>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package cloudy
import (
"bytes"
"encoding/json"
"github.com/CloudyKit/cloudy/link"
"github.com/CloudyKit/router"
"io"
"context"
"errors"
"fmt"
"net/http"
"net/url"
"reflect"
)
var ContextType = reflect.TypeOf((*Context)(nil))
// GetContext gets a Context from the registry context
func GetContext(cdi Registry) *Context {
return cdi.LoadType(ContextType).(*Context)
}
// Context holds context information about the incoming request
type Context struct {
Name string // The name associated with the route
Registry Registry // Dependency injection context
Request *http.Request // Request data passed by the router
Gen *link.URLGen
handlers []Handler
Response http.ResponseWriter // Response Writer passed by the router
Params router.Parameter // Route Registry passed by the router
body io.ReadCloser
bodyBytes []byte
bodyReady bool
}
func (c *Context) SendJSONWithStatus(v any, statusCode int) error {
c.Response.Header().Set("Content-Type", "application/json")
if statusCode == 0 {
statusCode = http.StatusOK
}
c.Response.WriteHeader(statusCode)
return json.NewEncoder(c.Response).Encode(v)
}
func (c *Context) SendJSON(v any) error {
return c.SendJSONWithStatus(v, http.StatusOK)
}
func (c *Context) GoContext() context.Context {
return c.Request.Context()
}
// Next will continue with the request flow
func (c *Context) Next() error {
if len(c.handlers) == 0 {
return errors.New("request.Context: no available handlers to advance")
}
// todo: with this behavior we can allow retry, a func can advance multiple times
// handlers := c.handlers
// c.handlers = c.handlers[1:]
// handlers[0].Handle(c)
// c.handlers = handlers
handler := c.handlers[0]
c.handlers = c.handlers[1:]
handler.Handle(c)
return nil
}
// WriteString writes the string txt into the the response
func (c *Context) WriteString(txt string) (int, error) {
return c.Response.Write([]byte(txt))
}
// Printf prints a formatted text to response writer
func (c *Context) Printf(format string, v ...interface{}) (int, error) {
return fmt.Fprintf(c.Response, format, v...)
}
// Redirect redirects the request to the specified urlStr and send a http StatusFound code
func (c *Context) Redirect(urlStr string) {
c.RedirectStatus(urlStr, http.StatusFound)
}
// RedirectStatus redirects the request to the specified urlStr and send the the status code specified by httpStatus
func (c *Context) RedirectStatus(urlStr string, httpStatus int) {
http.Redirect(c.Response, c.Request, urlStr, httpStatus)
}
// GetURLParameter returns a parameter from the url route, GetURLParameter is shortcut for Context.Params.Get method
func (c *Context) GetURLParameter(name string) string {
return c.Params.Get(name)
}
// GetPostValue returns a form value from the request, GetPostValue is shortcut for Context.Request.Form.Get method
func (c *Context) GetPostValue(name string) string {
if c.Request.PostForm == nil {
_ = c.Request.ParseForm()
}
return c.Request.PostForm.Get(name)
}
// GetGetValue returns a form value from the request, GetPostValue is shortcut for Context.Request.Form.Get method
func (c *Context) GetGetValue(name string) string {
if c.Request.Form == nil {
_ = c.Request.ParseForm()
}
return c.Request.Form.Get(name)
}
// GetCookieValue returns a cookie value from the request
func (c *Context) GetCookieValue(name string) (value string) {
if cookie, _ := c.Request.Cookie(name); cookie != nil {
value, _ = url.QueryUnescape(cookie.Value)
}
return
}
type contextBodyReader struct {
c *Context
buffer bytes.Reader
started bool
}
func (c *Context) GetBodyBytes() ([]byte, error) {
var err error
if !c.bodyReady {
c.bodyBytes, err = io.ReadAll(c.body)
if err != nil {
return nil, err
}
c.bodyReady = true
}
return c.bodyBytes, nil
}
func (c *contextBodyReader) Read(p []byte) (int, error) {
if !c.started {
bodyBytes, err := c.c.GetBodyBytes()
if err != nil {
return 0, err
}
c.started = true
c.buffer.Reset(bodyBytes)
}
return c.buffer.Read(p)
}
func (c *contextBodyReader) Close() error {
return c.c.body.Close()
}
// GetBodyReader returns bytes bodyReader
func (c *Context) GetBodyReader() io.ReadCloser {
return &contextBodyReader{
c: c,
}
}
func (c *Context) SendTextWithStatus(statusCode int, content string) error {
c.Response.Header().Set("Content-Type", "text/plain")
c.Response.WriteHeader(statusCode)
_, err := c.Response.Write([]byte(content))
return err
}