requests.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. package httpc
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/json"
  6. "fmt"
  7. "io"
  8. "net/http"
  9. nurl "net/url"
  10. "strings"
  11. "github.com/zeromicro/go-zero/core/lang"
  12. "github.com/zeromicro/go-zero/core/mapping"
  13. "github.com/zeromicro/go-zero/rest/httpc/internal"
  14. )
  15. var interceptors = []internal.Interceptor{
  16. internal.LogInterceptor,
  17. }
  18. // Do sends an HTTP request with the given arguments and returns an HTTP response.
  19. // data is automatically marshal into a *httpRequest, typically it's defined in an API file.
  20. func Do(ctx context.Context, method, url string, data interface{}) (*http.Response, error) {
  21. req, err := buildRequest(ctx, method, url, data)
  22. if err != nil {
  23. return nil, err
  24. }
  25. return DoRequest(req)
  26. }
  27. // DoRequest sends an HTTP request and returns an HTTP response.
  28. func DoRequest(r *http.Request) (*http.Response, error) {
  29. return request(r, defaultClient{})
  30. }
  31. type (
  32. client interface {
  33. do(r *http.Request) (*http.Response, error)
  34. }
  35. defaultClient struct{}
  36. )
  37. func (c defaultClient) do(r *http.Request) (*http.Response, error) {
  38. return http.DefaultClient.Do(r)
  39. }
  40. func buildFormQuery(u *nurl.URL, val map[string]interface{}) string {
  41. query := u.Query()
  42. for k, v := range val {
  43. query.Add(k, fmt.Sprint(v))
  44. }
  45. return query.Encode()
  46. }
  47. func buildRequest(ctx context.Context, method, url string, data interface{}) (*http.Request, error) {
  48. u, err := nurl.Parse(url)
  49. if err != nil {
  50. return nil, err
  51. }
  52. var val map[string]map[string]interface{}
  53. if data != nil {
  54. val, err = mapping.Marshal(data)
  55. if err != nil {
  56. return nil, err
  57. }
  58. }
  59. if err := fillPath(u, val[pathKey]); err != nil {
  60. return nil, err
  61. }
  62. var reader io.Reader
  63. jsonVars, hasJsonBody := val[jsonKey]
  64. if hasJsonBody {
  65. if method == http.MethodGet {
  66. return nil, ErrGetWithBody
  67. }
  68. var buf bytes.Buffer
  69. enc := json.NewEncoder(&buf)
  70. if err := enc.Encode(jsonVars); err != nil {
  71. return nil, err
  72. }
  73. reader = &buf
  74. }
  75. req, err := http.NewRequestWithContext(ctx, method, u.String(), reader)
  76. if err != nil {
  77. return nil, err
  78. }
  79. req.URL.RawQuery = buildFormQuery(u, val[formKey])
  80. fillHeader(req, val[headerKey])
  81. if hasJsonBody {
  82. req.Header.Set(contentType, applicationJson)
  83. }
  84. return req, nil
  85. }
  86. func fillHeader(r *http.Request, val map[string]interface{}) {
  87. for k, v := range val {
  88. r.Header.Add(k, fmt.Sprint(v))
  89. }
  90. }
  91. func fillPath(u *nurl.URL, val map[string]interface{}) error {
  92. used := make(map[string]lang.PlaceholderType)
  93. fields := strings.Split(u.Path, slash)
  94. for i := range fields {
  95. field := fields[i]
  96. if len(field) > 0 && field[0] == colon {
  97. name := field[1:]
  98. ival, ok := val[name]
  99. if !ok {
  100. return fmt.Errorf("missing path variable %q", name)
  101. }
  102. value := fmt.Sprint(ival)
  103. if len(value) == 0 {
  104. return fmt.Errorf("empty path variable %q", name)
  105. }
  106. fields[i] = value
  107. used[name] = lang.Placeholder
  108. }
  109. }
  110. if len(val) != len(used) {
  111. for key := range used {
  112. delete(val, key)
  113. }
  114. var unused []string
  115. for key := range val {
  116. unused = append(unused, key)
  117. }
  118. return fmt.Errorf("more path variables are provided: %q", strings.Join(unused, ", "))
  119. }
  120. u.Path = strings.Join(fields, slash)
  121. return nil
  122. }
  123. func request(r *http.Request, cli client) (*http.Response, error) {
  124. var respHandlers []internal.ResponseHandler
  125. for _, interceptor := range interceptors {
  126. var h internal.ResponseHandler
  127. r, h = interceptor(r)
  128. respHandlers = append(respHandlers, h)
  129. }
  130. resp, err := cli.do(r)
  131. for i := len(respHandlers) - 1; i >= 0; i-- {
  132. respHandlers[i](resp, err)
  133. }
  134. return resp, err
  135. }