-
Notifications
You must be signed in to change notification settings - Fork 2
/
request.go
157 lines (130 loc) · 3.69 KB
/
request.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
package supernova
import (
"encoding/json"
"errors"
"io"
"strings"
"github.com/valyala/fasthttp"
"golang.org/x/net/context"
)
// Request resembles an incoming request
type Request struct {
*fasthttp.RequestCtx
routeParams map[string]string
queryParams map[string]string
BaseUrl string
// Writer is used to write to response body
Writer io.Writer
Ctx context.Context
}
// JSONError resembles the RESTful standard for an error response
type JSONError struct {
Errors []interface{} `json:"errors"`
Code int `json:"code"`
Message string `json:"message"`
}
// JSONErrors holds the JSONError response
type JSONErrors struct {
Error JSONError `json:"error"`
}
// NewRequest creates a new Request pointer for an incoming request
func NewRequest(ctx *fasthttp.RequestCtx) *Request {
req := new(Request)
req.RequestCtx = ctx
req.routeParams = make(map[string]string)
req.queryParams = make(map[string]string)
req.BaseUrl = string(ctx.URI().Path())
req.Writer = ctx.Response.BodyWriter()
req.buildQueryParams()
return req
}
// RouteParam checks for and returns param or "" if doesn't exist
func (r *Request) RouteParam(key string) string {
if val, ok := r.routeParams[key]; ok {
return val
}
return ""
}
// QueryParam checks for and returns param or "" if doesn't exist
func (r *Request) QueryParam(key string) string {
if val, ok := r.queryParams[key]; ok {
return val
}
return ""
}
// Error allows an easy method to set the RESTful standard error response
func (r *Request) Error(statusCode int, msg string, errors ...interface{}) (int, error) {
r.Response.Reset()
newErr := JSONErrors{
Error: JSONError{
Errors: errors,
Code: statusCode,
Message: msg,
},
}
return r.JSON(statusCode, newErr)
}
// buildRouteParams builds a map of the route params
func (r *Request) buildRouteParams(route string) {
routeParams := r.routeParams
reqParts := strings.Split(r.BaseUrl[1:], "/")
routeParts := strings.Split(route[1:], "/")
for index, val := range routeParts {
if val[0] == ':' {
routeParams[val[1:]] = reqParts[index]
}
}
}
// buildQueryParams parses out all query params and places them in map
func (r *Request) buildQueryParams() {
r.RequestCtx.QueryArgs().VisitAll(func(key, value []byte) {
r.queryParams[string(key)] = string(value)
})
}
// ReadJSON unmarshals request body into the struct provided
func (r *Request) ReadJSON(i interface{}) error {
//TODO: detect body size and use reader if necessary
return json.Unmarshal(r.Request.Body(), i)
}
// Send writes the data to the response body
func (r *Request) Send(data interface{}) (int, error) {
switch v := data.(type) {
case []byte:
return r.Write(v)
case string:
return r.Write([]byte(v))
case error:
return r.Write([]byte(v.Error()))
}
return 0, errors.New("unsupported type")
}
// JSON marshals the given interface object and writes the JSON response.
func (r *Request) JSON(code int, obj interface{}) (int, error) {
jsn, err := json.Marshal(obj)
if err != nil {
return 0, err
}
r.Response.Header.Set("Content-Type", "application/json")
r.SetStatusCode(code)
return r.Write(jsn)
}
// GetMethod provides a simple way to return the request method type as a string
func (r *Request) GetMethod() string {
return string(r.Method())
}
// buildUrlParams builds url params and returns base route
func (r *Request) buildUrlParams() {
reqUrl := string(r.Request.RequestURI())
baseParts := strings.Split(reqUrl, "?")
if len(baseParts) == 0 {
return
}
params := strings.Join(baseParts[1:], "")
paramParts := strings.Split(params, "&")
for i := range paramParts {
keyValue := strings.Split(paramParts[i], "=")
if len(keyValue) > 1 {
r.routeParams[keyValue[0]] = keyValue[1]
}
}
}