requests.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. package httpx
  2. import (
  3. "fmt"
  4. "io"
  5. "net/http"
  6. "strings"
  7. "sync/atomic"
  8. "github.com/wuntsong-org/go-zero-plus/core/mapping"
  9. "github.com/wuntsong-org/go-zero-plus/core/validation"
  10. "github.com/wuntsong-org/go-zero-plus/rest/internal/encoding"
  11. "github.com/wuntsong-org/go-zero-plus/rest/internal/header"
  12. "github.com/wuntsong-org/go-zero-plus/rest/pathvar"
  13. )
  14. const (
  15. formKey = "form"
  16. pathKey = "path"
  17. maxMemory = 32 << 20 // 32MB
  18. maxBodyLen = 8 << 20 // 8MB
  19. separator = ";"
  20. tokensInAttribute = 2
  21. )
  22. var (
  23. formUnmarshaler = mapping.NewUnmarshaler(formKey, mapping.WithStringValues(), mapping.WithOpaqueKeys())
  24. pathUnmarshaler = mapping.NewUnmarshaler(pathKey, mapping.WithStringValues(), mapping.WithOpaqueKeys())
  25. validator atomic.Value
  26. )
  27. // Validator defines the interface for validating the request.
  28. type Validator interface {
  29. // Validate validates the request and parsed data.
  30. Validate(r *http.Request, data any) error
  31. }
  32. // Parse parses the request.
  33. func Parse(r *http.Request, v any) (err error) {
  34. defer func() {
  35. r := recover()
  36. if r == nil {
  37. return
  38. }
  39. tmp, ok := r.(error)
  40. if ok {
  41. err = tmp
  42. } else {
  43. err = fmt.Errorf("panic error: %v", r)
  44. }
  45. }()
  46. if err := ParsePath(r, v); err != nil {
  47. return err
  48. }
  49. if err := ParseForm(r, v); err != nil {
  50. return err
  51. }
  52. if err := ParseHeaders(r, v); err != nil {
  53. return err
  54. }
  55. if err := ParseJsonBody(r, v); err != nil {
  56. return err
  57. }
  58. if valid, ok := v.(validation.Validator); ok {
  59. return valid.Validate()
  60. } else if val := validator.Load(); val != nil {
  61. return val.(Validator).Validate(r, v)
  62. }
  63. return nil
  64. }
  65. // ParseHeaders parses the headers request.
  66. func ParseHeaders(r *http.Request, v any) error {
  67. return encoding.ParseHeaders(r.Header, v)
  68. }
  69. // ParseForm parses the form request.
  70. func ParseForm(r *http.Request, v any) error {
  71. params, err := GetFormValues(r)
  72. if err != nil {
  73. return err
  74. }
  75. return formUnmarshaler.Unmarshal(params, v)
  76. }
  77. // ParseHeader parses the request header and returns a map.
  78. func ParseHeader(headerValue string) map[string]string {
  79. ret := make(map[string]string)
  80. fields := strings.Split(headerValue, separator)
  81. for _, field := range fields {
  82. field = strings.TrimSpace(field)
  83. if len(field) == 0 {
  84. continue
  85. }
  86. kv := strings.SplitN(field, "=", tokensInAttribute)
  87. if len(kv) != tokensInAttribute {
  88. continue
  89. }
  90. ret[kv[0]] = kv[1]
  91. }
  92. return ret
  93. }
  94. // ParseJsonBody parses the post request which contains json in body.
  95. func ParseJsonBody(r *http.Request, v any) error {
  96. if withJsonBody(r) {
  97. reader := io.LimitReader(r.Body, maxBodyLen)
  98. return mapping.UnmarshalJsonReader(reader, v)
  99. }
  100. return mapping.UnmarshalJsonMap(nil, v)
  101. }
  102. // ParsePath parses the symbols reside in url path.
  103. // Like http://localhost/bag/:name
  104. func ParsePath(r *http.Request, v any) error {
  105. vars := pathvar.Vars(r)
  106. m := make(map[string]any, len(vars))
  107. for k, v := range vars {
  108. m[k] = v
  109. }
  110. return pathUnmarshaler.Unmarshal(m, v)
  111. }
  112. // SetValidator sets the validator.
  113. // The validator is used to validate the request, only called in Parse,
  114. // not in ParseHeaders, ParseForm, ParseHeader, ParseJsonBody, ParsePath.
  115. func SetValidator(val Validator) {
  116. validator.Store(val)
  117. }
  118. func withJsonBody(r *http.Request) bool {
  119. return r.ContentLength > 0 && strings.Contains(r.Header.Get(header.ContentType), header.ApplicationJson)
  120. }