forked from Laisky/go-utils
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhttp.go
119 lines (101 loc) · 3.11 KB
/
http.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package utils
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"strings"
"time"
"github.com/Laisky/go-chaining"
"github.com/Laisky/zap"
"github.com/json-iterator/go"
"github.com/pkg/errors"
)
var (
json = jsoniter.ConfigCompatibleWithStandardLibrary
httpClient = &http.Client{ // default http client
Transport: &http.Transport{
MaxIdleConnsPerHost: 20,
},
Timeout: time.Duration(30) * time.Second,
}
)
// RequestData 发起请求的结构体
type RequestData struct {
Headers map[string]string
Data interface{}
}
// RequestJSON request JSON and return JSON by default client
func RequestJSON(method, url string, request *RequestData, resp interface{}) (err error) {
return RequestJSONWithClient(httpClient, method, url, request, resp)
}
// RequestJSONWithClient request JSON and return JSON with specific client
func RequestJSONWithClient(httpClient *http.Client, method, url string, request *RequestData, resp interface{}) (err error) {
Logger.Debug("try to request with json", zap.String("method", method), zap.String("url", url))
var (
jsonBytes []byte
)
jsonBytes, err = json.Marshal(request.Data)
if err != nil {
return errors.Wrap(err, "marshal request data error")
}
Logger.Debug("request json", zap.String("body", string(jsonBytes[:])))
req, err := http.NewRequest(strings.ToUpper(method), url, bytes.NewBuffer(jsonBytes))
req.Header.Set(HTTPJSONHeader, HTTPJSONHeaderVal)
for k, v := range request.Headers {
req.Header.Set(k, v)
}
r, err := httpClient.Do(req)
if err != nil {
return errors.Wrap(err, "try to request url error")
}
defer r.Body.Close()
if FloorDivision(r.StatusCode, 100) != 2 {
respBytes, err := ioutil.ReadAll(r.Body)
if err != nil {
return errors.Wrap(err, "try to read response data error")
}
return errors.New(string(respBytes[:]))
}
respBytes, err := ioutil.ReadAll(r.Body)
if err != nil {
return errors.Wrap(err, "try to read response data error")
}
Logger.Debug("got resp", zap.ByteString("resp", respBytes))
err = json.Unmarshal(respBytes, resp)
if err != nil {
errMsg := fmt.Sprintf("try to unmarshal response data error: %v\n%v", err, string(respBytes[:]))
return errors.Wrap(err, errMsg)
}
Logger.Debug("request json successed", zap.String("body", string(respBytes[:])))
return nil
}
// CheckResp check HTTP response's status code and return the error with body message
func CheckResp(resp *http.Response) error {
c := chaining.Flow(
checkRespStatus,
checkRespBody,
)(resp, nil)
return c.GetError()
}
func checkRespStatus(c *chaining.Chain) (r interface{}, err error) {
resp := c.GetVal()
code := resp.(*http.Response).StatusCode
if FloorDivision(code, 100) != 2 {
return resp, HTTPInvalidStatusError(code)
}
return resp, nil
}
func checkRespBody(c *chaining.Chain) (interface{}, error) {
upErr := c.GetError()
resp := c.GetVal().(*http.Response)
if upErr == nil {
return c.GetVal(), nil
}
defer resp.Body.Close()
respB, err := ioutil.ReadAll(resp.Body)
if err != nil {
return resp, errors.Wrapf(upErr, "read body got error: %v", err.Error())
}
return resp, errors.Wrapf(upErr, "got http body: %v", string(respB[:]))
}