|
|
|
package httpx
|
|
|
|
|
|
|
|
import (
|
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/tal-tech/go-zero/core/mapping"
|
|
|
|
"github.com/tal-tech/go-zero/rest/internal/context"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
multipartFormData = "multipart/form-data"
|
|
|
|
formKey = "form"
|
|
|
|
pathKey = "path"
|
|
|
|
emptyJson = "{}"
|
|
|
|
maxMemory = 32 << 20 // 32MB
|
|
|
|
maxBodyLen = 8 << 20 // 8MB
|
|
|
|
separator = ";"
|
|
|
|
tokensInAttribute = 2
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
formUnmarshaler = mapping.NewUnmarshaler(formKey, mapping.WithStringValues())
|
|
|
|
pathUnmarshaler = mapping.NewUnmarshaler(pathKey, mapping.WithStringValues())
|
|
|
|
)
|
|
|
|
|
|
|
|
func Parse(r *http.Request, v interface{}) error {
|
|
|
|
if err := ParsePath(r, v); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := ParseForm(r, v); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if r.ContentLength > 0 {
|
|
|
|
return ParseJsonBody(r, v)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parses the form request.
|
|
|
|
func ParseForm(r *http.Request, v interface{}) error {
|
|
|
|
if strings.Contains(r.Header.Get(ContentType), multipartFormData) {
|
|
|
|
if err := r.ParseMultipartForm(maxMemory); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if err := r.ParseForm(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
params := make(map[string]interface{}, len(r.Form))
|
|
|
|
for name := range r.Form {
|
|
|
|
formValue := r.Form.Get(name)
|
|
|
|
if len(formValue) > 0 {
|
|
|
|
params[name] = formValue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return formUnmarshaler.Unmarshal(params, v)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ParseHeader(headerValue string) map[string]string {
|
|
|
|
ret := make(map[string]string)
|
|
|
|
fields := strings.Split(headerValue, separator)
|
|
|
|
|
|
|
|
for _, field := range fields {
|
|
|
|
field = strings.TrimSpace(field)
|
|
|
|
if len(field) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
kv := strings.SplitN(field, "=", tokensInAttribute)
|
|
|
|
if len(kv) != tokensInAttribute {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
ret[kv[0]] = kv[1]
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parses the post request which contains json in body.
|
|
|
|
func ParseJsonBody(r *http.Request, v interface{}) error {
|
|
|
|
switch r.Method {
|
|
|
|
case http.MethodDelete, http.MethodPatch, http.MethodPost, http.MethodPut:
|
|
|
|
default:
|
|
|
|
return ErrBodylessRequest
|
|
|
|
}
|
|
|
|
|
|
|
|
var reader io.Reader
|
|
|
|
if withJsonBody(r) {
|
|
|
|
reader = io.LimitReader(r.Body, maxBodyLen)
|
|
|
|
} else {
|
|
|
|
reader = strings.NewReader(emptyJson)
|
|
|
|
}
|
|
|
|
|
|
|
|
return mapping.UnmarshalJsonReader(reader, v)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parses the symbols reside in url path.
|
|
|
|
// Like http://localhost/bag/:name
|
|
|
|
func ParsePath(r *http.Request, v interface{}) error {
|
|
|
|
vars := context.Vars(r)
|
|
|
|
m := make(map[string]interface{}, len(vars))
|
|
|
|
for k, v := range vars {
|
|
|
|
m[k] = v
|
|
|
|
}
|
|
|
|
|
|
|
|
return pathUnmarshaler.Unmarshal(m, v)
|
|
|
|
}
|
|
|
|
|
|
|
|
func withJsonBody(r *http.Request) bool {
|
|
|
|
return r.ContentLength > 0 && strings.Contains(r.Header.Get(ContentType), ApplicationJson)
|
|
|
|
}
|