github.com/gogf/gf@v1.16.9/net/ghttp/ghttp_request_param.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/gogf/gf. 6 7 package ghttp 8 9 import ( 10 "bytes" 11 "fmt" 12 "io/ioutil" 13 "mime/multipart" 14 "reflect" 15 "strings" 16 17 "github.com/gogf/gf/container/gvar" 18 "github.com/gogf/gf/encoding/gjson" 19 "github.com/gogf/gf/encoding/gurl" 20 "github.com/gogf/gf/encoding/gxml" 21 "github.com/gogf/gf/errors/gcode" 22 "github.com/gogf/gf/errors/gerror" 23 "github.com/gogf/gf/internal/json" 24 "github.com/gogf/gf/internal/utils" 25 "github.com/gogf/gf/text/gregex" 26 "github.com/gogf/gf/text/gstr" 27 "github.com/gogf/gf/util/gconv" 28 "github.com/gogf/gf/util/gvalid" 29 ) 30 31 const ( 32 parseTypeRequest = 0 33 parseTypeQuery = 1 34 parseTypeForm = 2 35 ) 36 37 var ( 38 // xmlHeaderBytes is the most common XML format header. 39 xmlHeaderBytes = []byte("<?xml") 40 ) 41 42 // Parse is the most commonly used function, which converts request parameters to struct or struct 43 // slice. It also automatically validates the struct or every element of the struct slice according 44 // to the validation tag of the struct. 45 // 46 // The parameter <pointer> can be type of: *struct/**struct/*[]struct/*[]*struct. 47 // 48 // It supports single and multiple struct convertion: 49 // 1. Single struct, post content like: {"id":1, "name":"john"} or ?id=1&name=john 50 // 2. Multiple struct, post content like: [{"id":1, "name":"john"}, {"id":, "name":"smith"}] 51 // 52 // TODO: Improve the performance by reducing duplicated reflect usage on the same variable across packages. 53 func (r *Request) Parse(pointer interface{}) error { 54 return r.doParse(pointer, parseTypeRequest) 55 } 56 57 // ParseQuery performs like function Parse, but only parses the query parameters. 58 func (r *Request) ParseQuery(pointer interface{}) error { 59 return r.doParse(pointer, parseTypeQuery) 60 } 61 62 // ParseForm performs like function Parse, but only parses the form parameters or the body content. 63 func (r *Request) ParseForm(pointer interface{}) error { 64 return r.doParse(pointer, parseTypeForm) 65 } 66 67 // doParse parses the request data to struct/structs according to request type. 68 func (r *Request) doParse(pointer interface{}, requestType int) error { 69 var ( 70 reflectVal1 = reflect.ValueOf(pointer) 71 reflectKind1 = reflectVal1.Kind() 72 ) 73 if reflectKind1 != reflect.Ptr { 74 return fmt.Errorf( 75 "parameter should be type of *struct/**struct/*[]struct/*[]*struct, but got: %v", 76 reflectKind1, 77 ) 78 } 79 var ( 80 reflectVal2 = reflectVal1.Elem() 81 reflectKind2 = reflectVal2.Kind() 82 ) 83 switch reflectKind2 { 84 // Single struct, post content like: 85 // 1. {"id":1, "name":"john"} 86 // 2. ?id=1&name=john 87 case reflect.Ptr, reflect.Struct: 88 var ( 89 err error 90 data map[string]interface{} 91 ) 92 // Converting. 93 switch requestType { 94 case parseTypeQuery: 95 if data, err = r.doGetQueryStruct(pointer); err != nil { 96 return err 97 } 98 case parseTypeForm: 99 if data, err = r.doGetFormStruct(pointer); err != nil { 100 return err 101 } 102 default: 103 if data, err = r.doGetRequestStruct(pointer); err != nil { 104 return err 105 } 106 } 107 // Validation. 108 if err := gvalid.CheckStructWithData(r.Context(), pointer, data, nil); err != nil { 109 return err 110 } 111 112 // Multiple struct, it only supports JSON type post content like: 113 // [{"id":1, "name":"john"}, {"id":, "name":"smith"}] 114 case reflect.Array, reflect.Slice: 115 // If struct slice conversion, it might post JSON/XML content, 116 // so it uses `gjson` for the conversion. 117 j, err := gjson.LoadContent(r.GetBody()) 118 if err != nil { 119 return err 120 } 121 if err := j.GetStructs(".", pointer); err != nil { 122 return err 123 } 124 for i := 0; i < reflectVal2.Len(); i++ { 125 if err := gvalid.CheckStructWithData( 126 r.Context(), 127 reflectVal2.Index(i), 128 j.GetMap(gconv.String(i)), 129 nil, 130 ); err != nil { 131 return err 132 } 133 } 134 } 135 return nil 136 } 137 138 // Get is alias of GetRequest, which is one of the most commonly used functions for 139 // retrieving parameter. 140 // See r.GetRequest. 141 func (r *Request) Get(key string, def ...interface{}) interface{} { 142 return r.GetRequest(key, def...) 143 } 144 145 // GetVar is alis of GetRequestVar. 146 // See GetRequestVar. 147 func (r *Request) GetVar(key string, def ...interface{}) *gvar.Var { 148 return r.GetRequestVar(key, def...) 149 } 150 151 // GetRaw is alias of GetBody. 152 // See GetBody. 153 // Deprecated, use GetBody instead. 154 func (r *Request) GetRaw() []byte { 155 return r.GetBody() 156 } 157 158 // GetRawString is alias of GetBodyString. 159 // See GetBodyString. 160 // Deprecated, use GetBodyString instead. 161 func (r *Request) GetRawString() string { 162 return r.GetBodyString() 163 } 164 165 // GetBody retrieves and returns request body content as bytes. 166 // It can be called multiple times retrieving the same body content. 167 func (r *Request) GetBody() []byte { 168 if r.bodyContent == nil { 169 r.bodyContent, _ = ioutil.ReadAll(r.Body) 170 r.Body = utils.NewReadCloser(r.bodyContent, true) 171 } 172 return r.bodyContent 173 } 174 175 // GetBodyString retrieves and returns request body content as string. 176 // It can be called multiple times retrieving the same body content. 177 func (r *Request) GetBodyString() string { 178 return string(r.GetBody()) 179 } 180 181 // GetJson parses current request content as JSON format, and returns the JSON object. 182 // Note that the request content is read from request BODY, not from any field of FORM. 183 func (r *Request) GetJson() (*gjson.Json, error) { 184 return gjson.LoadJson(r.GetBody()) 185 } 186 187 // GetString is an alias and convenient function for GetRequestString. 188 // See GetRequestString. 189 func (r *Request) GetString(key string, def ...interface{}) string { 190 return r.GetRequestString(key, def...) 191 } 192 193 // GetBool is an alias and convenient function for GetRequestBool. 194 // See GetRequestBool. 195 func (r *Request) GetBool(key string, def ...interface{}) bool { 196 return r.GetRequestBool(key, def...) 197 } 198 199 // GetInt is an alias and convenient function for GetRequestInt. 200 // See GetRequestInt. 201 func (r *Request) GetInt(key string, def ...interface{}) int { 202 return r.GetRequestInt(key, def...) 203 } 204 205 // GetInt32 is an alias and convenient function for GetRequestInt32. 206 // See GetRequestInt32. 207 func (r *Request) GetInt32(key string, def ...interface{}) int32 { 208 return r.GetRequestInt32(key, def...) 209 } 210 211 // GetInt64 is an alias and convenient function for GetRequestInt64. 212 // See GetRequestInt64. 213 func (r *Request) GetInt64(key string, def ...interface{}) int64 { 214 return r.GetRequestInt64(key, def...) 215 } 216 217 // GetInts is an alias and convenient function for GetRequestInts. 218 // See GetRequestInts. 219 func (r *Request) GetInts(key string, def ...interface{}) []int { 220 return r.GetRequestInts(key, def...) 221 } 222 223 // GetUint is an alias and convenient function for GetRequestUint. 224 // See GetRequestUint. 225 func (r *Request) GetUint(key string, def ...interface{}) uint { 226 return r.GetRequestUint(key, def...) 227 } 228 229 // GetUint32 is an alias and convenient function for GetRequestUint32. 230 // See GetRequestUint32. 231 func (r *Request) GetUint32(key string, def ...interface{}) uint32 { 232 return r.GetRequestUint32(key, def...) 233 } 234 235 // GetUint64 is an alias and convenient function for GetRequestUint64. 236 // See GetRequestUint64. 237 func (r *Request) GetUint64(key string, def ...interface{}) uint64 { 238 return r.GetRequestUint64(key, def...) 239 } 240 241 // GetFloat32 is an alias and convenient function for GetRequestFloat32. 242 // See GetRequestFloat32. 243 func (r *Request) GetFloat32(key string, def ...interface{}) float32 { 244 return r.GetRequestFloat32(key, def...) 245 } 246 247 // GetFloat64 is an alias and convenient function for GetRequestFloat64. 248 // See GetRequestFloat64. 249 func (r *Request) GetFloat64(key string, def ...interface{}) float64 { 250 return r.GetRequestFloat64(key, def...) 251 } 252 253 // GetFloats is an alias and convenient function for GetRequestFloats. 254 // See GetRequestFloats. 255 func (r *Request) GetFloats(key string, def ...interface{}) []float64 { 256 return r.GetRequestFloats(key, def...) 257 } 258 259 // GetArray is an alias and convenient function for GetRequestArray. 260 // See GetRequestArray. 261 func (r *Request) GetArray(key string, def ...interface{}) []string { 262 return r.GetRequestArray(key, def...) 263 } 264 265 // GetStrings is an alias and convenient function for GetRequestStrings. 266 // See GetRequestStrings. 267 func (r *Request) GetStrings(key string, def ...interface{}) []string { 268 return r.GetRequestStrings(key, def...) 269 } 270 271 // GetInterfaces is an alias and convenient function for GetRequestInterfaces. 272 // See GetRequestInterfaces. 273 func (r *Request) GetInterfaces(key string, def ...interface{}) []interface{} { 274 return r.GetRequestInterfaces(key, def...) 275 } 276 277 // GetMap is an alias and convenient function for GetRequestMap. 278 // See GetRequestMap. 279 func (r *Request) GetMap(def ...map[string]interface{}) map[string]interface{} { 280 return r.GetRequestMap(def...) 281 } 282 283 // GetMapStrStr is an alias and convenient function for GetRequestMapStrStr. 284 // See GetRequestMapStrStr. 285 func (r *Request) GetMapStrStr(def ...map[string]interface{}) map[string]string { 286 return r.GetRequestMapStrStr(def...) 287 } 288 289 // GetStruct is an alias and convenient function for GetRequestStruct. 290 // See GetRequestStruct. 291 func (r *Request) GetStruct(pointer interface{}, mapping ...map[string]string) error { 292 return r.GetRequestStruct(pointer, mapping...) 293 } 294 295 // parseQuery parses query string into r.queryMap. 296 func (r *Request) parseQuery() { 297 if r.parsedQuery { 298 return 299 } 300 r.parsedQuery = true 301 if r.URL.RawQuery != "" { 302 var err error 303 r.queryMap, err = gstr.Parse(r.URL.RawQuery) 304 if err != nil { 305 panic(gerror.WrapCode(gcode.CodeInvalidParameter, err, "")) 306 } 307 } 308 } 309 310 // parseBody parses the request raw data into r.rawMap. 311 // Note that it also supports JSON data from client request. 312 func (r *Request) parseBody() { 313 if r.parsedBody { 314 return 315 } 316 r.parsedBody = true 317 // There's no data posted. 318 if r.ContentLength == 0 { 319 return 320 } 321 if body := r.GetBody(); len(body) > 0 { 322 // Trim space/new line characters. 323 body = bytes.TrimSpace(body) 324 // JSON format checks. 325 if body[0] == '{' && body[len(body)-1] == '}' { 326 _ = json.UnmarshalUseNumber(body, &r.bodyMap) 327 } 328 // XML format checks. 329 if len(body) > 5 && bytes.EqualFold(body[:5], xmlHeaderBytes) { 330 r.bodyMap, _ = gxml.DecodeWithoutRoot(body) 331 } 332 if body[0] == '<' && body[len(body)-1] == '>' { 333 r.bodyMap, _ = gxml.DecodeWithoutRoot(body) 334 } 335 // Default parameters decoding. 336 if r.bodyMap == nil { 337 r.bodyMap, _ = gstr.Parse(r.GetBodyString()) 338 } 339 } 340 } 341 342 // parseForm parses the request form for HTTP method PUT, POST, PATCH. 343 // The form data is pared into r.formMap. 344 // 345 // Note that if the form was parsed firstly, the request body would be cleared and empty. 346 func (r *Request) parseForm() { 347 if r.parsedForm { 348 return 349 } 350 r.parsedForm = true 351 // There's no data posted. 352 if r.ContentLength == 0 { 353 return 354 } 355 if contentType := r.Header.Get("Content-Type"); contentType != "" { 356 var err error 357 if gstr.Contains(contentType, "multipart/") { 358 // multipart/form-data, multipart/mixed 359 if err = r.ParseMultipartForm(r.Server.config.FormParsingMemory); err != nil { 360 panic(gerror.WrapCode(gcode.CodeInvalidRequest, err, "")) 361 } 362 } else if gstr.Contains(contentType, "form") { 363 // application/x-www-form-urlencoded 364 if err = r.Request.ParseForm(); err != nil { 365 panic(gerror.WrapCode(gcode.CodeInvalidRequest, err, "")) 366 } 367 } 368 if len(r.PostForm) > 0 { 369 // Re-parse the form data using united parsing way. 370 params := "" 371 for name, values := range r.PostForm { 372 // Invalid parameter name. 373 // Only allow chars of: '\w', '[', ']', '-'. 374 if !gregex.IsMatchString(`^[\w\-\[\]]+$`, name) && len(r.PostForm) == 1 { 375 // It might be JSON/XML content. 376 if s := gstr.Trim(name + strings.Join(values, " ")); len(s) > 0 { 377 if s[0] == '{' && s[len(s)-1] == '}' || s[0] == '<' && s[len(s)-1] == '>' { 378 r.bodyContent = []byte(s) 379 params = "" 380 break 381 } 382 } 383 } 384 if len(values) == 1 { 385 if len(params) > 0 { 386 params += "&" 387 } 388 params += name + "=" + gurl.Encode(values[0]) 389 } else { 390 if len(name) > 2 && name[len(name)-2:] == "[]" { 391 name = name[:len(name)-2] 392 for _, v := range values { 393 if len(params) > 0 { 394 params += "&" 395 } 396 params += name + "[]=" + gurl.Encode(v) 397 } 398 } else { 399 if len(params) > 0 { 400 params += "&" 401 } 402 params += name + "=" + gurl.Encode(values[len(values)-1]) 403 } 404 } 405 } 406 if params != "" { 407 if r.formMap, err = gstr.Parse(params); err != nil { 408 panic(gerror.WrapCode(gcode.CodeInvalidParameter, err, "")) 409 } 410 } 411 } 412 } 413 // It parses the request body without checking the Content-Type. 414 if r.formMap == nil { 415 if r.Method != "GET" { 416 r.parseBody() 417 } 418 if len(r.bodyMap) > 0 { 419 r.formMap = r.bodyMap 420 } 421 } 422 } 423 424 // GetMultipartForm parses and returns the form as multipart form. 425 func (r *Request) GetMultipartForm() *multipart.Form { 426 r.parseForm() 427 return r.MultipartForm 428 } 429 430 // GetMultipartFiles parses and returns the post files array. 431 // Note that the request form should be type of multipart. 432 func (r *Request) GetMultipartFiles(name string) []*multipart.FileHeader { 433 form := r.GetMultipartForm() 434 if form == nil { 435 return nil 436 } 437 if v := form.File[name]; len(v) > 0 { 438 return v 439 } 440 // Support "name[]" as array parameter. 441 if v := form.File[name+"[]"]; len(v) > 0 { 442 return v 443 } 444 // Support "name[0]","name[1]","name[2]", etc. as array parameter. 445 key := "" 446 files := make([]*multipart.FileHeader, 0) 447 for i := 0; ; i++ { 448 key = fmt.Sprintf(`%s[%d]`, name, i) 449 if v := form.File[key]; len(v) > 0 { 450 files = append(files, v[0]) 451 } else { 452 break 453 } 454 } 455 if len(files) > 0 { 456 return files 457 } 458 return nil 459 }