123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- package httpx
- import (
- "fmt"
- "io"
- "net/http"
- "strings"
- "sync/atomic"
- "github.com/wuntsong-org/go-zero-plus/core/mapping"
- "github.com/wuntsong-org/go-zero-plus/core/validation"
- "github.com/wuntsong-org/go-zero-plus/rest/internal/encoding"
- "github.com/wuntsong-org/go-zero-plus/rest/internal/header"
- "github.com/wuntsong-org/go-zero-plus/rest/pathvar"
- )
- 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())
- pathUnmarshaler = mapping.NewUnmarshaler(pathKey, mapping.WithStringValues(), mapping.WithOpaqueKeys())
- validator atomic.Value
- )
- // 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
- }
- // Parse parses the request.
- func Parse(r *http.Request, v any) (err error) {
- defer func() {
- r := recover()
- if r == nil {
- return
- }
- tmp, ok := r.(error)
- if ok {
- err = tmp
- } else {
- err = fmt.Errorf("panic error: %v", r)
- }
- }()
- 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
- }
- // ParseHeaders parses the headers request.
- func ParseHeaders(r *http.Request, v any) error {
- return encoding.ParseHeaders(r.Header, v)
- }
- // ParseForm parses the form request.
- func ParseForm(r *http.Request, v any) error {
- params, err := GetFormValues(r)
- if err != nil {
- return err
- }
- return formUnmarshaler.Unmarshal(params, v)
- }
- // ParseHeader parses the request header and returns a map.
- 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
- }
- // ParseJsonBody parses the post request which contains json in body.
- func ParseJsonBody(r *http.Request, v any) error {
- if withJsonBody(r) {
- reader := io.LimitReader(r.Body, maxBodyLen)
- return mapping.UnmarshalJsonReader(reader, v)
- }
- return mapping.UnmarshalJsonMap(nil, v)
- }
- // ParsePath parses the symbols reside in url path.
- // Like http://localhost/bag/:name
- func ParsePath(r *http.Request, v any) error {
- vars := pathvar.Vars(r)
- m := make(map[string]any, len(vars))
- 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)
- }
- func withJsonBody(r *http.Request) bool {
- return r.ContentLength > 0 && strings.Contains(r.Header.Get(header.ContentType), header.ApplicationJson)
- }
|