// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. package ghttp import ( "bytes" "fmt" "io/ioutil" "mime/multipart" "reflect" "strings" "github.com/gogf/gf/v2/container/gvar" "github.com/gogf/gf/v2/encoding/gjson" "github.com/gogf/gf/v2/encoding/gurl" "github.com/gogf/gf/v2/encoding/gxml" "github.com/gogf/gf/v2/errors/gcode" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/internal/json" "github.com/gogf/gf/v2/internal/utils" "github.com/gogf/gf/v2/text/gregex" "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/v2/util/gconv" "github.com/gogf/gf/v2/util/gvalid" ) const ( parseTypeRequest = 0 parseTypeQuery = 1 parseTypeForm = 2 ) var ( // xmlHeaderBytes is the most common XML format header. xmlHeaderBytes = []byte(" 0 { // Trim space/new line characters. body = bytes.TrimSpace(body) // JSON format checks. if body[0] == '{' && body[len(body)-1] == '}' { _ = json.UnmarshalUseNumber(body, &r.bodyMap) } // XML format checks. if len(body) > 5 && bytes.EqualFold(body[:5], xmlHeaderBytes) { r.bodyMap, _ = gxml.DecodeWithoutRoot(body) } if body[0] == '<' && body[len(body)-1] == '>' { r.bodyMap, _ = gxml.DecodeWithoutRoot(body) } // Default parameters decoding. if r.bodyMap == nil { r.bodyMap, _ = gstr.Parse(r.GetBodyString()) } } } // parseForm parses the request form for HTTP method PUT, POST, PATCH. // The form data is pared into r.formMap. // // Note that if the form was parsed firstly, the request body would be cleared and empty. func (r *Request) parseForm() { if r.parsedForm { return } r.parsedForm = true // There's no data posted. if r.ContentLength == 0 { return } if contentType := r.Header.Get("Content-Type"); contentType != "" { var err error if gstr.Contains(contentType, "multipart/") { // multipart/form-data, multipart/mixed if err = r.ParseMultipartForm(r.Server.config.FormParsingMemory); err != nil { panic(gerror.WrapCode(gcode.CodeInvalidRequest, err, "r.ParseMultipartForm failed")) } } else if gstr.Contains(contentType, "form") { // application/x-www-form-urlencoded if err = r.Request.ParseForm(); err != nil { panic(gerror.WrapCode(gcode.CodeInvalidRequest, err, "r.Request.ParseForm failed")) } } if len(r.PostForm) > 0 { // Re-parse the form data using united parsing way. params := "" for name, values := range r.PostForm { // Invalid parameter name. // Only allow chars of: '\w', '[', ']', '-'. if !gregex.IsMatchString(`^[\w\-\[\]]+$`, name) && len(r.PostForm) == 1 { // It might be JSON/XML content. if s := gstr.Trim(name + strings.Join(values, " ")); len(s) > 0 { if s[0] == '{' && s[len(s)-1] == '}' || s[0] == '<' && s[len(s)-1] == '>' { r.bodyContent = []byte(s) params = "" break } } } if len(values) == 1 { if len(params) > 0 { params += "&" } params += name + "=" + gurl.Encode(values[0]) } else { if len(name) > 2 && name[len(name)-2:] == "[]" { name = name[:len(name)-2] for _, v := range values { if len(params) > 0 { params += "&" } params += name + "[]=" + gurl.Encode(v) } } else { if len(params) > 0 { params += "&" } params += name + "=" + gurl.Encode(values[len(values)-1]) } } } if params != "" { if r.formMap, err = gstr.Parse(params); err != nil { panic(gerror.WrapCode(gcode.CodeInvalidParameter, err, "Parse request parameters failed")) } } } } // It parses the request body without checking the Content-Type. if r.formMap == nil { if r.Method != "GET" { r.parseBody() } if len(r.bodyMap) > 0 { r.formMap = r.bodyMap } } } // GetMultipartForm parses and returns the form as multipart forms. func (r *Request) GetMultipartForm() *multipart.Form { r.parseForm() return r.MultipartForm } // GetMultipartFiles parses and returns the post files array. // Note that the request form should be type of multipart. func (r *Request) GetMultipartFiles(name string) []*multipart.FileHeader { form := r.GetMultipartForm() if form == nil { return nil } if v := form.File[name]; len(v) > 0 { return v } // Support "name[]" as array parameter. if v := form.File[name+"[]"]; len(v) > 0 { return v } // Support "name[0]","name[1]","name[2]", etc. as array parameter. var ( key = "" files = make([]*multipart.FileHeader, 0) ) for i := 0; ; i++ { key = fmt.Sprintf(`%s[%d]`, name, i) if v := form.File[key]; len(v) > 0 { files = append(files, v[0]) } else { break } } if len(files) > 0 { return files } return nil }