You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
134 lines
3.1 KiB
134 lines
3.1 KiB
package httpx |
|
|
|
import ( |
|
"fmt" |
|
"git.diulo.com/mogfee/kit/core/mapping" |
|
"git.diulo.com/mogfee/kit/rest/pathvar" |
|
"io" |
|
"net/http" |
|
"strings" |
|
"sync/atomic" |
|
) |
|
|
|
const ( |
|
formKey = "form" |
|
jsonKey = "json" |
|
pathKey = "path" |
|
maxMemory = 32 << 20 // 32MB |
|
maxBodyLen = 8 << 20 // 8MB |
|
separator = ";" |
|
tokensInAttribute = 2 |
|
) |
|
|
|
var ( |
|
formUnmarshaler = mapping.NewUnmarshaler(formKey, mapping.WithStringValues()) |
|
pathUnmarshaler = mapping.NewUnmarshaler(pathKey, mapping.WithStringValues()) |
|
queryUnmarshaler = mapping.NewUnmarshaler(jsonKey, mapping.WithStringValues()) |
|
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) error { |
|
if err := ParsePath(r, v); err != nil { |
|
fmt.Println("ParsePath") |
|
return err |
|
} |
|
|
|
if err := ParseForm(r, v); err != nil { |
|
fmt.Println("ParseForm") |
|
return err |
|
|
|
} |
|
|
|
if err := ParseHeaders(r, v); err != nil { |
|
fmt.Println("ParseHeaders") |
|
return err |
|
} |
|
|
|
if err := ParseJsonBody(r, v); err != nil { |
|
fmt.Println("ParseJsonBody") |
|
return err |
|
} |
|
|
|
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 mapping.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 { |
|
fmt.Println("withJsonBody(r)", withJsonBody(r)) |
|
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(ContentType), ApplicationJson) |
|
}
|
|
|