github.com/System-Glitch/goyave/v3@v3.6.1-0.20210226143142-ac2fe42ee80e/request.go (about) 1 package goyave 2 3 import ( 4 "net" 5 "net/http" 6 "net/url" 7 "strings" 8 "time" 9 10 "github.com/System-Glitch/goyave/v3/cors" 11 "github.com/imdario/mergo" 12 13 "github.com/System-Glitch/goyave/v3/helper/filesystem" 14 "github.com/System-Glitch/goyave/v3/validation" 15 "github.com/google/uuid" 16 ) 17 18 // Request struct represents an http request. 19 // Contains the validated body in the Data attribute if the route was defined with a request generator function 20 type Request struct { 21 httpRequest *http.Request 22 corsOptions *cors.Options 23 cookies []*http.Cookie 24 route *Route 25 User interface{} 26 Rules *validation.Rules 27 Data map[string]interface{} 28 Extra map[string]interface{} 29 Params map[string]string 30 Lang string 31 } 32 33 // Request return the raw http request. 34 // Prefer using the "goyave.Request" accessors. 35 func (r *Request) Request() *http.Request { 36 return r.httpRequest 37 } 38 39 // Method specifies the HTTP method (GET, POST, PUT, etc.). 40 func (r *Request) Method() string { 41 return r.httpRequest.Method 42 } 43 44 // Protocol the protocol used by this request, "HTTP/1.1" for example. 45 func (r *Request) Protocol() string { 46 return r.httpRequest.Proto 47 } 48 49 // URI specifies the URI being requested. 50 // Use this if you absolutely need the raw query params, url, etc. 51 // Otherwise use the provided methods and fields of the "goyave.Request". 52 func (r *Request) URI() *url.URL { 53 return r.httpRequest.URL 54 } 55 56 // Route returns the current route. 57 func (r *Request) Route() *Route { 58 return r.route 59 } 60 61 // Header contains the request header fields either received 62 // by the server or to be sent by the client. 63 // Header names are case-insensitive. 64 // 65 // If the raw request has the following header lines, 66 // 67 // Host: example.com 68 // accept-encoding: gzip, deflate 69 // Accept-Language: en-us 70 // fOO: Bar 71 // foo: two 72 // 73 // then the header map will look like this: 74 // 75 // Header = map[string][]string{ 76 // "Accept-Encoding": {"gzip, deflate"}, 77 // "Accept-Language": {"en-us"}, 78 // "Foo": {"Bar", "two"}, 79 // } 80 func (r *Request) Header() http.Header { 81 return r.httpRequest.Header 82 } 83 84 // ContentLength records the length of the associated content. 85 // The value -1 indicates that the length is unknown. 86 func (r *Request) ContentLength() int64 { 87 return r.httpRequest.ContentLength 88 } 89 90 // RemoteAddress allows to record the network address that 91 // sent the request, usually for logging. 92 func (r *Request) RemoteAddress() string { 93 return r.httpRequest.RemoteAddr 94 } 95 96 // Cookies returns the HTTP cookies sent with the request. 97 func (r *Request) Cookies(name string) []*http.Cookie { 98 if r.cookies == nil { 99 r.cookies = r.httpRequest.Cookies() 100 } 101 return r.cookies 102 } 103 104 // Referrer returns the referring URL, if sent in the request. 105 func (r *Request) Referrer() string { 106 return r.httpRequest.Referer() 107 } 108 109 // UserAgent returns the client's User-Agent, if sent in the request. 110 func (r *Request) UserAgent() string { 111 return r.httpRequest.UserAgent() 112 } 113 114 // BasicAuth returns the username and password provided in the request's 115 // Authorization header, if the request uses HTTP Basic Authentication. 116 func (r *Request) BasicAuth() (username, password string, ok bool) { 117 return r.httpRequest.BasicAuth() 118 } 119 120 // BearerToken extract the auth token from the "Authorization" header. 121 // Only takes tokens of type "Bearer". 122 // Returns empty string if no token found or the header is invalid. 123 func (r *Request) BearerToken() (string, bool) { 124 const schema = "Bearer " 125 header := r.Header().Get("Authorization") 126 if !strings.HasPrefix(header, schema) { 127 return "", false 128 } 129 return strings.TrimSpace(header[len(schema):]), true 130 } 131 132 // CORSOptions returns the CORS options applied to this request, or nil. 133 // The returned object is a copy of the options applied to the router. 134 // Therefore, altering the returned object will not alter the router's options. 135 func (r *Request) CORSOptions() *cors.Options { 136 if r.corsOptions == nil { 137 return nil 138 } 139 140 cpy := *r.corsOptions 141 return &cpy 142 } 143 144 // Has check if the given field exists in the request data. 145 func (r *Request) Has(field string) bool { 146 _, exists := r.Data[field] 147 return exists 148 } 149 150 // String get a string field from the request data. 151 // Panics if the field is not a string. 152 func (r *Request) String(field string) string { 153 str, ok := r.Data[field].(string) 154 if !ok { 155 ErrLogger.Panicf("Field \"%s\" is not a string", field) 156 } 157 return str 158 } 159 160 // Numeric get a numeric field from the request data. 161 // Panics if the field is not numeric. 162 func (r *Request) Numeric(field string) float64 { 163 str, ok := r.Data[field].(float64) 164 if !ok { 165 ErrLogger.Panicf("Field \"%s\" is not numeric", field) 166 } 167 return str 168 } 169 170 // Integer get an integer field from the request data. 171 // Panics if the field is not an integer. 172 func (r *Request) Integer(field string) int { 173 str, ok := r.Data[field].(int) 174 if !ok { 175 ErrLogger.Panicf("Field \"%s\" is not an integer", field) 176 } 177 return str 178 } 179 180 // Bool get a bool field from the request data. 181 // Panics if the field is not a bool. 182 func (r *Request) Bool(field string) bool { 183 str, ok := r.Data[field].(bool) 184 if !ok { 185 ErrLogger.Panicf("Field \"%s\" is not a bool", field) 186 } 187 return str 188 } 189 190 // File get a file field from the request data. 191 // Panics if the field is not numeric. 192 func (r *Request) File(field string) []filesystem.File { 193 str, ok := r.Data[field].([]filesystem.File) 194 if !ok { 195 ErrLogger.Panicf("Field \"%s\" is not a file", field) 196 } 197 return str 198 } 199 200 // Timezone get a timezone field from the request data. 201 // Panics if the field is not a timezone. 202 func (r *Request) Timezone(field string) *time.Location { 203 str, ok := r.Data[field].(*time.Location) 204 if !ok { 205 ErrLogger.Panicf("Field \"%s\" is not a timezone", field) 206 } 207 return str 208 } 209 210 // IP get an IP field from the request data. 211 // Panics if the field is not an IP. 212 func (r *Request) IP(field string) net.IP { 213 str, ok := r.Data[field].(net.IP) 214 if !ok { 215 ErrLogger.Panicf("Field \"%s\" is not an IP", field) 216 } 217 return str 218 } 219 220 // URL get a URL field from the request data. 221 // Panics if the field is not a URL. 222 func (r *Request) URL(field string) *url.URL { 223 str, ok := r.Data[field].(*url.URL) 224 if !ok { 225 ErrLogger.Panicf("Field \"%s\" is not a URL", field) 226 } 227 return str 228 } 229 230 // UUID get a UUID field from the request data. 231 // Panics if the field is not a UUID. 232 func (r *Request) UUID(field string) uuid.UUID { 233 str, ok := r.Data[field].(uuid.UUID) 234 if !ok { 235 ErrLogger.Panicf("Field \"%s\" is not an UUID", field) 236 } 237 return str 238 } 239 240 // Date get a date field from the request data. 241 // Panics if the field is not a date. 242 func (r *Request) Date(field string) time.Time { 243 str, ok := r.Data[field].(time.Time) 244 if !ok { 245 ErrLogger.Panicf("Field \"%s\" is not a date", field) 246 } 247 return str 248 } 249 250 // Object get an object field from the request data. 251 // Panics if the field is not an object. 252 func (r *Request) Object(field string) map[string]interface{} { 253 str, ok := r.Data[field].(map[string]interface{}) 254 if !ok { 255 ErrLogger.Panicf("Field \"%s\" is not an object", field) 256 } 257 return str 258 } 259 260 // ToStruct map the request data to a struct. 261 // type UserInsertRequest struct { 262 // Username string 263 // Email string 264 // } 265 // //... 266 // userInsertRequest := UserInsertRequest{} 267 // if err := request.ToStruct(&userInsertRequest); err != nil { 268 // panic(err) 269 // } 270 func (r *Request) ToStruct(dst interface{}) error { 271 return mergo.Map(dst, r.Data) 272 } 273 274 func (r *Request) validate() validation.Errors { 275 if r.Rules == nil { 276 return nil 277 } 278 279 contentType := r.httpRequest.Header.Get("Content-Type") 280 errors := validation.Validate(r.Data, r.Rules, strings.HasPrefix(contentType, "application/json"), r.Lang) 281 if len(errors) > 0 { 282 return errors 283 } 284 285 return nil 286 }