go-zero/rest/httpx/requests.go

140 lines
3.1 KiB
Go
Raw Normal View History

2020-07-26 17:09:05 +08:00
package httpx
import (
"io"
"net/http"
"strings"
"sync/atomic"
2020-07-26 17:09:05 +08:00
"github.com/zeromicro/go-zero/core/mapping"
"github.com/zeromicro/go-zero/core/validation"
2022-03-23 17:58:21 +08:00
"github.com/zeromicro/go-zero/rest/internal/encoding"
"github.com/zeromicro/go-zero/rest/internal/header"
"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 (
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
)
// Validator defines the interface for validating the request.
type Validator interface {
// Validate validates the request and parsed data.
Validate(r *http.Request, data any) error
}
2021-02-09 13:50:21 +08:00
// Parse parses the request.
func Parse(r *http.Request, v any) error {
2020-07-26 17:09:05 +08:00
if err := ParsePath(r, v); err != nil {
return err
}
if err := ParseForm(r, v); err != nil {
return err
}
if err := ParseHeaders(r, v); err != nil {
return err
}
if err := ParseJsonBody(r, v); err != nil {
return err
}
if valid, ok := v.(validation.Validator); ok {
return valid.Validate()
} else if val := validator.Load(); val != nil {
return val.(Validator).Validate(r, v)
}
return nil
2020-07-26 17:09:05 +08:00
}
// ParseHeaders parses the headers request.
func ParseHeaders(r *http.Request, v any) error {
2022-03-23 17:58:21 +08:00
return encoding.ParseHeaders(r.Header, v)
}
2021-02-09 13:50:21 +08:00
// ParseForm parses the form request.
func ParseForm(r *http.Request, v any) error {
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.
func ParseJsonBody(r *http.Request, v any) error {
2020-07-26 17:09:05 +08:00
if withJsonBody(r) {
reader := io.LimitReader(r.Body, maxBodyLen)
return mapping.UnmarshalJsonReader(reader, v)
2020-07-26 17:09:05 +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
func ParsePath(r *http.Request, v any) error {
vars := pathvar.Vars(r)
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)
}
// 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 {
return r.ContentLength > 0 && strings.Contains(r.Header.Get(header.ContentType), header.ApplicationJson)
2020-07-26 17:09:05 +08:00
}