2020-07-26 17:09:05 +08:00
|
|
|
package httpx
|
|
|
|
|
|
|
|
import (
|
|
|
|
"io"
|
|
|
|
"net/http"
|
2024-11-05 22:09:30 +08:00
|
|
|
"reflect"
|
2020-07-26 17:09:05 +08:00
|
|
|
"strings"
|
2023-02-26 21:58:58 +08:00
|
|
|
"sync/atomic"
|
2020-07-26 17:09:05 +08:00
|
|
|
|
2022-01-04 15:51:32 +08:00
|
|
|
"github.com/zeromicro/go-zero/core/mapping"
|
2023-03-19 20:04:18 +08:00
|
|
|
"github.com/zeromicro/go-zero/core/validation"
|
2022-03-23 17:58:21 +08:00
|
|
|
"github.com/zeromicro/go-zero/rest/internal/encoding"
|
2022-04-28 15:12:04 +08:00
|
|
|
"github.com/zeromicro/go-zero/rest/internal/header"
|
2022-01-04 15:51:32 +08:00
|
|
|
"github.com/zeromicro/go-zero/rest/pathvar"
|
2020-07-26 17:09:05 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
formKey = "form"
|
|
|
|
pathKey = "path"
|
|
|
|
maxMemory = 32 << 20 // 32MB
|
|
|
|
maxBodyLen = 8 << 20 // 8MB
|
|
|
|
separator = ";"
|
|
|
|
tokensInAttribute = 2
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2024-11-02 21:55:37 +08:00
|
|
|
formUnmarshaler = mapping.NewUnmarshaler(
|
|
|
|
formKey,
|
|
|
|
mapping.WithStringValues(),
|
|
|
|
mapping.WithOpaqueKeys(),
|
|
|
|
mapping.WithFromArray())
|
|
|
|
pathUnmarshaler = mapping.NewUnmarshaler(
|
|
|
|
pathKey,
|
|
|
|
mapping.WithStringValues(),
|
|
|
|
mapping.WithOpaqueKeys())
|
|
|
|
validator atomic.Value
|
2020-07-26 17:09:05 +08:00
|
|
|
)
|
|
|
|
|
2023-02-26 21:58:58 +08:00
|
|
|
// Validator defines the interface for validating the request.
|
2023-02-26 20:40:22 +08:00
|
|
|
type Validator interface {
|
2023-02-26 21:58:58 +08:00
|
|
|
// Validate validates the request and parsed data.
|
|
|
|
Validate(r *http.Request, data any) error
|
2023-02-26 20:40:22 +08:00
|
|
|
}
|
|
|
|
|
2021-02-09 13:50:21 +08:00
|
|
|
// Parse parses the request.
|
2023-01-24 16:32:02 +08:00
|
|
|
func Parse(r *http.Request, v any) error {
|
2024-11-05 22:09:30 +08:00
|
|
|
kind := mapping.Deref(reflect.TypeOf(v)).Kind()
|
|
|
|
if kind != reflect.Array && kind != reflect.Slice {
|
|
|
|
if err := ParsePath(r, v); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-07-26 17:09:05 +08:00
|
|
|
|
2024-11-05 22:09:30 +08:00
|
|
|
if err := ParseForm(r, v); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-07-26 17:09:05 +08:00
|
|
|
|
2024-11-05 22:09:30 +08:00
|
|
|
if err := ParseHeaders(r, v); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-07-07 23:20:09 +08:00
|
|
|
}
|
|
|
|
|
2023-02-26 20:40:22 +08:00
|
|
|
if err := ParseJsonBody(r, v); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-03-19 20:04:18 +08:00
|
|
|
if valid, ok := v.(validation.Validator); ok {
|
|
|
|
return valid.Validate()
|
|
|
|
} else if val := validator.Load(); val != nil {
|
2023-02-26 21:58:58 +08:00
|
|
|
return val.(Validator).Validate(r, v)
|
2023-02-26 20:40:22 +08:00
|
|
|
}
|
2023-02-26 21:58:58 +08:00
|
|
|
|
2023-02-26 20:40:22 +08:00
|
|
|
return nil
|
2020-07-26 17:09:05 +08:00
|
|
|
}
|
|
|
|
|
2021-07-07 23:20:09 +08:00
|
|
|
// ParseHeaders parses the headers request.
|
2023-01-24 16:32:02 +08:00
|
|
|
func ParseHeaders(r *http.Request, v any) error {
|
2022-03-23 17:58:21 +08:00
|
|
|
return encoding.ParseHeaders(r.Header, v)
|
2021-07-07 23:20:09 +08:00
|
|
|
}
|
|
|
|
|
2021-02-09 13:50:21 +08:00
|
|
|
// ParseForm parses the form request.
|
2023-01-24 16:32:02 +08:00
|
|
|
func ParseForm(r *http.Request, v any) error {
|
2022-07-16 23:40:53 +08:00
|
|
|
params, err := GetFormValues(r)
|
|
|
|
if err != nil {
|
2020-12-02 15:00:07 +08:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-07-26 17:09:05 +08:00
|
|
|
return formUnmarshaler.Unmarshal(params, v)
|
|
|
|
}
|
|
|
|
|
2021-03-01 19:15:35 +08:00
|
|
|
// ParseHeader parses the request header and returns a map.
|
2020-07-26 17:09:05 +08:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2021-02-09 13:50:21 +08:00
|
|
|
// ParseJsonBody parses the post request which contains json in body.
|
2023-01-24 16:32:02 +08:00
|
|
|
func ParseJsonBody(r *http.Request, v any) error {
|
2020-07-26 17:09:05 +08:00
|
|
|
if withJsonBody(r) {
|
2021-12-22 21:43:37 +08:00
|
|
|
reader := io.LimitReader(r.Body, maxBodyLen)
|
2021-12-22 20:24:55 +08:00
|
|
|
return mapping.UnmarshalJsonReader(reader, v)
|
2020-07-26 17:09:05 +08:00
|
|
|
}
|
|
|
|
|
2021-12-22 21:43:37 +08:00
|
|
|
return mapping.UnmarshalJsonMap(nil, v)
|
2020-07-26 17:09:05 +08:00
|
|
|
}
|
|
|
|
|
2021-02-09 13:50:21 +08:00
|
|
|
// ParsePath parses the symbols reside in url path.
|
2020-07-26 17:09:05 +08:00
|
|
|
// Like http://localhost/bag/:name
|
2023-01-24 16:32:02 +08:00
|
|
|
func ParsePath(r *http.Request, v any) error {
|
2021-08-14 22:57:28 +08:00
|
|
|
vars := pathvar.Vars(r)
|
2023-01-24 16:32:02 +08:00
|
|
|
m := make(map[string]any, len(vars))
|
2020-07-26 17:09:05 +08:00
|
|
|
for k, v := range vars {
|
|
|
|
m[k] = v
|
|
|
|
}
|
|
|
|
|
|
|
|
return pathUnmarshaler.Unmarshal(m, v)
|
|
|
|
}
|
|
|
|
|
2023-02-26 21:58:58 +08:00
|
|
|
// SetValidator sets the validator.
|
|
|
|
// The validator is used to validate the request, only called in Parse,
|
|
|
|
// not in ParseHeaders, ParseForm, ParseHeader, ParseJsonBody, ParsePath.
|
|
|
|
func SetValidator(val Validator) {
|
|
|
|
validator.Store(val)
|
|
|
|
}
|
|
|
|
|
2020-07-26 17:09:05 +08:00
|
|
|
func withJsonBody(r *http.Request) bool {
|
2022-04-28 15:12:04 +08:00
|
|
|
return r.ContentLength > 0 && strings.Contains(r.Header.Get(header.ContentType), header.ApplicationJson)
|
2020-07-26 17:09:05 +08:00
|
|
|
}
|