├── .gitignore ├── examples └── main.go ├── go.mod ├── readme-zh.md ├── readme.md └── requests.go /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .idea/ 4 | 5 | go.sum 6 | -------------------------------------------------------------------------------- /examples/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | "net/url" 8 | "time" 9 | 10 | "github.com/xuanbo/requests" 11 | ) 12 | 13 | func main() { 14 | getRow() 15 | getText() 16 | postForm() 17 | postJson() 18 | postMultipart() 19 | handler() 20 | save() 21 | getJson() 22 | customHttp() 23 | requestInterceptor() 24 | } 25 | 26 | func getRow() { 27 | raw, err := requests.Get("http://127.0.0.1:8080/ping"). 28 | Params(url.Values{ 29 | "param1": {"value1"}, 30 | "param2": {"123"}, 31 | }). 32 | Send(). 33 | Raw() 34 | if err != nil { 35 | panic(err) 36 | } 37 | fmt.Println(raw) 38 | } 39 | 40 | func getText() { 41 | text, err := requests.Get("http://127.0.0.1:8080/ping"). 42 | Params(url.Values{ 43 | "param1": {"value1"}, 44 | "param2": {"123"}, 45 | }). 46 | Send(). 47 | Text() 48 | if err != nil { 49 | panic(err) 50 | } 51 | fmt.Println(text) 52 | } 53 | 54 | func postForm() { 55 | text, err := requests.Post("http://127.0.0.1:8080/ping"). 56 | Params(url.Values{ 57 | "param1": {"value1"}, 58 | "param2": {"123"}, 59 | }). 60 | Form(url.Values{ 61 | "form1": {"value1"}, 62 | "form2": {"123"}, 63 | }). 64 | Send(). 65 | Text() 66 | if err != nil { 67 | panic(err) 68 | } 69 | fmt.Println(text) 70 | } 71 | 72 | func postJson() { 73 | text, err := requests.Post("http://127.0.0.1:8080/ping"). 74 | Params(url.Values{ 75 | "param1": {"value1"}, 76 | "param2": {"123"}, 77 | }). 78 | Json(map[string]interface{}{ 79 | "json1": "value1", 80 | "json2": 2, 81 | }). 82 | Send(). 83 | Text() 84 | if err != nil { 85 | panic(err) 86 | } 87 | fmt.Println(text) 88 | } 89 | 90 | func postMultipart() { 91 | text, err := requests.Post("http://127.0.0.1:8080/ping"). 92 | Params(url.Values{ 93 | "param1": {"value1"}, 94 | "param2": {"123"}, 95 | }). 96 | Multipart(requests.FileForm{ 97 | Value: url.Values{ 98 | "form1": {"value1"}, 99 | "form2": {"value2"}, 100 | }, 101 | File: map[string]string{ 102 | "file1": "./examples/main.go", 103 | "file2": "./examples/main.go", 104 | }, 105 | }). 106 | Send(). 107 | Text() 108 | if err != nil { 109 | panic(err) 110 | } 111 | fmt.Println(text) 112 | } 113 | 114 | func save() { 115 | err := requests.Get("https://www.cnblogs.com/bener/p/10683404.html"). 116 | Send(). 117 | Save("./10683404.html") 118 | if err != nil { 119 | panic(err) 120 | } 121 | } 122 | 123 | func getJson() { 124 | var v map[string]interface{} 125 | err := requests.Post("http://127.0.0.1:8080/ping"). 126 | Params(url.Values{ 127 | "param1": {"value1"}, 128 | "param2": {"123"}, 129 | }). 130 | Json(map[string]interface{}{ 131 | "json1": "value1", 132 | "json2": 2, 133 | }). 134 | Send(). 135 | Json(&v) 136 | if err != nil { 137 | panic(err) 138 | } 139 | fmt.Println(v) 140 | } 141 | 142 | func handler() { 143 | result := requests.Post("http://127.0.0.1:8080/ping"). 144 | Params(url.Values{ 145 | "param1": {"value1"}, 146 | "param2": {"123"}, 147 | }). 148 | Json(map[string]interface{}{ 149 | "json1": "value1", 150 | "json2": 2, 151 | }). 152 | Send() 153 | if result.Err != nil { 154 | panic(result.Err) 155 | } 156 | 157 | b, err := ioutil.ReadAll(result.Resp.Body) 158 | if err != nil { 159 | panic(err) 160 | } 161 | defer result.Resp.Body.Close() 162 | 163 | fmt.Println(string(b)) 164 | } 165 | 166 | func customHttp() { 167 | client := &http.Client{ 168 | Timeout: 5 * time.Second, 169 | } 170 | text, err := requests.Request("https://github.com/xuanbo", "OPTIONS", client). 171 | Send(). 172 | Text() 173 | if err != nil { 174 | panic(err) 175 | } 176 | fmt.Println(text) 177 | } 178 | 179 | func requestInterceptor() { 180 | logRequestInterceptor := func(request *http.Request) error { 181 | fmt.Println(request.URL) 182 | return nil 183 | } 184 | requests.AddRequestInterceptors(logRequestInterceptor) 185 | 186 | text, err := requests.Get("https://github.com/xuanbo"). 187 | Send(). 188 | Text() 189 | if err != nil { 190 | panic(err) 191 | } 192 | fmt.Println(text) 193 | } 194 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/xuanbo/requests 2 | 3 | go 1.12 4 | -------------------------------------------------------------------------------- /readme-zh.md: -------------------------------------------------------------------------------- 1 | # requests 2 | 3 | > golang http请求库,类似python requests。 4 | 5 | ## 特点 6 | 7 | * `GET`、`POST`、`PUT`、`DELETE`(Common HTTP methods) 8 | * `application/json`、`application/x-www-form-urlencoded`、`multipart/form-data` 9 | * 全局请求拦截器 10 | 11 | ## 例子 12 | 13 | ### get请求 14 | 15 | ```go 16 | func getText() { 17 | text, err := requests.Get("http://127.0.0.1:8080/ping"). 18 | Params(url.Values{ 19 | "param1": {"value1"}, 20 | "param2": {"123"}, 21 | }). 22 | Send(). 23 | Text() 24 | if err != nil { 25 | panic(err) 26 | } 27 | fmt.Println(text) 28 | } 29 | ``` 30 | 31 | http报文: 32 | 33 | ``` 34 | GET http://127.0.0.1:8080/ping?param1=value1¶m2=123 HTTP/1.1 35 | ``` 36 | 37 | ### 表单请求 38 | 39 | ```go 40 | func postForm() { 41 | text, err := requests.Post("http://127.0.0.1:8080/ping"). 42 | Params(url.Values{ 43 | "param1": {"value1"}, 44 | "param2": {"123"}, 45 | }). 46 | Form(url.Values{ 47 | "form1": {"value1"}, 48 | "form2": {"123"}, 49 | }). 50 | Send(). 51 | Text() 52 | if err != nil { 53 | panic(err) 54 | } 55 | fmt.Println(text) 56 | } 57 | ``` 58 | 59 | http报文: 60 | 61 | ``` 62 | POST http://127.0.0.1:8080/ping?param1=value1¶m2=123 HTTP/1.1 63 | Content-Type: application/x-www-form-urlencoded 64 | 65 | form1=value1&form2=123 66 | ``` 67 | 68 | ### json请求 69 | 70 | ```go 71 | func postJson() { 72 | text, err := requests.Post("http://127.0.0.1:8080/ping"). 73 | Params(url.Values{ 74 | "param1": {"value1"}, 75 | "param2": {"123"}, 76 | }). 77 | Json(map[string]interface{}{ 78 | "json1": "value1", 79 | "json2": 2, 80 | }). 81 | Send(). 82 | Text() 83 | if err != nil { 84 | panic(err) 85 | } 86 | fmt.Println(text) 87 | } 88 | ``` 89 | 90 | http报文: 91 | 92 | ``` 93 | POST http://127.0.0.1:8080/ping?param1=value1¶m2=123 HTTP/1.1 94 | Content-Type: application/json 95 | 96 | {"json1": "value1", "json2": 2} 97 | ``` 98 | 99 | ### 文件上传 100 | 101 | ```go 102 | func postMultipart() { 103 | text, err := requests.Post("http://127.0.0.1:8080/ping"). 104 | Params(url.Values{ 105 | "param1": {"value1"}, 106 | "param2": {"123"}, 107 | }). 108 | Multipart(requests.FileForm{ 109 | Value: url.Values{ 110 | "form1": {"value1"}, 111 | "form2": {"value2"}, 112 | }, 113 | File: map[string]string{ 114 | "file1": "./examples/main.go", 115 | "file2": "./examples/main.go", 116 | }, 117 | }). 118 | Send(). 119 | Text() 120 | if err != nil { 121 | panic(err) 122 | } 123 | fmt.Println(text) 124 | } 125 | ``` 126 | 127 | http报文: 128 | 129 | ``` 130 | POST http://127.0.0.1:8080/ping?param1=value1¶m2=123 HTTP/1.1 131 | Content-Type: multipart/form-data; boundary=947f4ca12d44786ccda8f8cd60e083fca2ec1ede6d8f1bad69f4cf03bc8a 132 | 133 | --947f4ca12d44786ccda8f8cd60e083fca2ec1ede6d8f1bad69f4cf03bc8a 134 | Content-Disposition: form-data; name="file1"; filename="./examples/main.go" 135 | Content-Type: application/octet-stream 136 | 137 | bytes... 138 | 139 | --947f4ca12d44786ccda8f8cd60e083fca2ec1ede6d8f1bad69f4cf03bc8a 140 | Content-Disposition: form-data; name="file2"; filename="./examples/main.go" 141 | Content-Type: application/octet-stream 142 | 143 | bytes... 144 | 145 | --947f4ca12d44786ccda8f8cd60e083fca2ec1ede6d8f1bad69f4cf03bc8a 146 | Content-Disposition: form-data; name="form1" 147 | 148 | value1 149 | --947f4ca12d44786ccda8f8cd60e083fca2ec1ede6d8f1bad69f4cf03bc8a 150 | Content-Disposition: form-data; name="form2" 151 | 152 | value2 153 | --947f4ca12d44786ccda8f8cd60e083fca2ec1ede6d8f1bad69f4cf03bc8a-- 154 | ``` 155 | 156 | ### 保存文件 157 | 158 | ```go 159 | func save() { 160 | err := requests.Get("https://github.com/xuanbo/requests"). 161 | Send(). 162 | Save("./requests.html") 163 | if err != nil { 164 | panic(err) 165 | } 166 | } 167 | ``` 168 | 169 | ### 检查响应状态码 170 | 171 | ```go 172 | func save() { 173 | err := requests.Get("https://github.com/xuanbo/requests"). 174 | Send(). 175 | // resp status code must be 200. 176 | StatusOk(). 177 | Save("./requests.html") 178 | if err != nil { 179 | panic(err) 180 | } 181 | } 182 | ``` 183 | 184 | * `StatusOk()` 185 | * `Status2xx()` 186 | 187 | ### 自定义http 188 | 189 | ```go 190 | func customHttp() { 191 | client := &http.Client{ 192 | Timeout: 5 * time.Second, 193 | } 194 | text, err := requests.Request("https://github.com/xuanbo", "OPTIONS", client). 195 | Send(). 196 | Text() 197 | if err != nil { 198 | panic(err) 199 | } 200 | fmt.Println(text) 201 | } 202 | ``` 203 | 204 | ### 全局请求拦截器 205 | 206 | ```go 207 | func requestInterceptor() { 208 | logRequestInterceptor := func(request *http.Request) error { 209 | fmt.Println(request.URL) 210 | return nil 211 | } 212 | requests.AddRequestInterceptors(logRequestInterceptor) 213 | 214 | text, err := requests.Get("https://github.com/xuanbo"). 215 | Send(). 216 | Text() 217 | if err != nil { 218 | panic(err) 219 | } 220 | fmt.Println(text) 221 | } 222 | ``` -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # requests 2 | 3 | > http requests lib for golang 4 | 5 | ## Features 6 | 7 | * `GET`、`POST`、`PUT`、`DELETE`(Common HTTP methods) 8 | * `application/json`、`application/x-www-form-urlencoded`、`multipart/form-data` 9 | * Global Request Interceptor 10 | 11 | ## Examples 12 | 13 | ### Get 14 | 15 | ```go 16 | func getText() { 17 | text, err := requests.Get("http://127.0.0.1:8080/ping"). 18 | Params(url.Values{ 19 | "param1": {"value1"}, 20 | "param2": {"123"}, 21 | }). 22 | Send(). 23 | Text() 24 | if err != nil { 25 | panic(err) 26 | } 27 | fmt.Println(text) 28 | } 29 | ``` 30 | 31 | query 32 | 33 | ``` 34 | GET http://127.0.0.1:8080/ping?param1=value1¶m2=123 HTTP/1.1 35 | ``` 36 | 37 | ### Post Form 38 | 39 | ```go 40 | func postForm() { 41 | text, err := requests.Post("http://127.0.0.1:8080/ping"). 42 | Params(url.Values{ 43 | "param1": {"value1"}, 44 | "param2": {"123"}, 45 | }). 46 | Form(url.Values{ 47 | "form1": {"value1"}, 48 | "form2": {"123"}, 49 | }). 50 | Send(). 51 | Text() 52 | if err != nil { 53 | panic(err) 54 | } 55 | fmt.Println(text) 56 | } 57 | ``` 58 | 59 | post form 60 | 61 | ``` 62 | POST http://127.0.0.1:8080/ping?param1=value1¶m2=123 HTTP/1.1 63 | Content-Type: application/x-www-form-urlencoded 64 | 65 | form1=value1&form2=123 66 | ``` 67 | 68 | ### Post Json 69 | 70 | ```go 71 | func postJson() { 72 | text, err := requests.Post("http://127.0.0.1:8080/ping"). 73 | Params(url.Values{ 74 | "param1": {"value1"}, 75 | "param2": {"123"}, 76 | }). 77 | Json(map[string]interface{}{ 78 | "json1": "value1", 79 | "json2": 2, 80 | }). 81 | Send(). 82 | Text() 83 | if err != nil { 84 | panic(err) 85 | } 86 | fmt.Println(text) 87 | } 88 | ``` 89 | 90 | post json 91 | 92 | ``` 93 | POST http://127.0.0.1:8080/ping?param1=value1¶m2=123 HTTP/1.1 94 | Content-Type: application/json 95 | 96 | {"json1": "value1", "json2": 2} 97 | ``` 98 | 99 | ### Post Multipart 100 | 101 | ```go 102 | func postMultipart() { 103 | text, err := requests.Post("http://127.0.0.1:8080/ping"). 104 | Params(url.Values{ 105 | "param1": {"value1"}, 106 | "param2": {"123"}, 107 | }). 108 | Multipart(requests.FileForm{ 109 | Value: url.Values{ 110 | "form1": {"value1"}, 111 | "form2": {"value2"}, 112 | }, 113 | File: map[string]string{ 114 | "file1": "./examples/main.go", 115 | "file2": "./examples/main.go", 116 | }, 117 | }). 118 | Send(). 119 | Text() 120 | if err != nil { 121 | panic(err) 122 | } 123 | fmt.Println(text) 124 | } 125 | ``` 126 | 127 | post multipart/form-data 128 | 129 | ``` 130 | POST http://127.0.0.1:8080/ping?param1=value1¶m2=123 HTTP/1.1 131 | Content-Type: multipart/form-data; boundary=947f4ca12d44786ccda8f8cd60e083fca2ec1ede6d8f1bad69f4cf03bc8a 132 | 133 | --947f4ca12d44786ccda8f8cd60e083fca2ec1ede6d8f1bad69f4cf03bc8a 134 | Content-Disposition: form-data; name="file1"; filename="./examples/main.go" 135 | Content-Type: application/octet-stream 136 | 137 | bytes... 138 | 139 | --947f4ca12d44786ccda8f8cd60e083fca2ec1ede6d8f1bad69f4cf03bc8a 140 | Content-Disposition: form-data; name="file2"; filename="./examples/main.go" 141 | Content-Type: application/octet-stream 142 | 143 | bytes... 144 | 145 | --947f4ca12d44786ccda8f8cd60e083fca2ec1ede6d8f1bad69f4cf03bc8a 146 | Content-Disposition: form-data; name="form1" 147 | 148 | value1 149 | --947f4ca12d44786ccda8f8cd60e083fca2ec1ede6d8f1bad69f4cf03bc8a 150 | Content-Disposition: form-data; name="form2" 151 | 152 | value2 153 | --947f4ca12d44786ccda8f8cd60e083fca2ec1ede6d8f1bad69f4cf03bc8a-- 154 | ``` 155 | 156 | ### Save File 157 | 158 | ```go 159 | func save() { 160 | err := requests.Get("https://github.com/xuanbo/requests"). 161 | Send(). 162 | Save("./requests.html") 163 | if err != nil { 164 | panic(err) 165 | } 166 | } 167 | ``` 168 | 169 | ### Check Status Code 170 | 171 | ```go 172 | func save() { 173 | err := requests.Get("https://github.com/xuanbo/requests"). 174 | Send(). 175 | // resp status code must be 200. 176 | StatusOk(). 177 | Save("./requests.html") 178 | if err != nil { 179 | panic(err) 180 | } 181 | } 182 | ``` 183 | 184 | * `StatusOk()` 185 | * `Status2xx()` 186 | 187 | ### Custom Http 188 | 189 | ```go 190 | func customHttp() { 191 | client := &http.Client{ 192 | Timeout: 5 * time.Second, 193 | } 194 | text, err := requests.Request("https://github.com/xuanbo", "OPTIONS", client). 195 | Send(). 196 | Text() 197 | if err != nil { 198 | panic(err) 199 | } 200 | fmt.Println(text) 201 | } 202 | ``` 203 | 204 | ### Global Request Interceptor 205 | 206 | ```go 207 | func requestInterceptor() { 208 | logRequestInterceptor := func(request *http.Request) error { 209 | fmt.Println(request.URL) 210 | return nil 211 | } 212 | requests.AddRequestInterceptors(logRequestInterceptor) 213 | 214 | text, err := requests.Get("https://github.com/xuanbo"). 215 | Send(). 216 | Text() 217 | if err != nil { 218 | panic(err) 219 | } 220 | fmt.Println(text) 221 | } 222 | ``` -------------------------------------------------------------------------------- /requests.go: -------------------------------------------------------------------------------- 1 | package requests 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "io" 8 | "io/ioutil" 9 | "mime/multipart" 10 | "net/http" 11 | "net/url" 12 | "os" 13 | "strings" 14 | "sync" 15 | ) 16 | 17 | const ( 18 | ContentType = "Content-Type" 19 | ApplicationJSON = "application/json" 20 | ApplicationFormUrlencoded = "application/x-www-form-urlencoded" 21 | ) 22 | 23 | // RequestInterceptor 请求拦截器 24 | // 返回不为nil,即有错误会终止后续执行 25 | type RequestInterceptor func(request *http.Request) error 26 | 27 | // requestInterceptorChain 请求拦截链 28 | type requestInterceptorChain struct { 29 | mutex *sync.RWMutex 30 | interceptors []RequestInterceptor 31 | } 32 | 33 | // defaultRequestInterceptorChain 默认的请求拦截链实例 34 | var defaultRequestInterceptorChain = &requestInterceptorChain{ 35 | mutex: new(sync.RWMutex), 36 | interceptors: make([]RequestInterceptor, 0), 37 | } 38 | 39 | // Client 封装了http的参数等信息 40 | type Client struct { 41 | // 自定义Client 42 | client *http.Client 43 | 44 | url string 45 | method string 46 | header http.Header 47 | params url.Values 48 | 49 | form url.Values 50 | json interface{} 51 | multipart FileForm 52 | } 53 | 54 | // FileForm form参数和文件参数 55 | type FileForm struct { 56 | Value url.Values 57 | File map[string]string 58 | } 59 | 60 | // Result http响应结果 61 | type Result struct { 62 | Resp *http.Response 63 | Err error 64 | } 65 | 66 | // Get http `GET` 请求 67 | func Get(url string) *Client { 68 | return newClient(url, http.MethodGet, nil) 69 | } 70 | 71 | // Post http `POST` 请求 72 | func Post(url string) *Client { 73 | return newClient(url, http.MethodPost, nil) 74 | } 75 | 76 | // Put http `PUT` 请求 77 | func Put(url string) *Client { 78 | return newClient(url, http.MethodPut, nil) 79 | } 80 | 81 | // Delete http `DELETE` 请求 82 | func Delete(url string) *Client { 83 | return newClient(url, http.MethodDelete, nil) 84 | } 85 | 86 | // Request 用于自定义请求方式,比如`HEAD`、`PATCH`、`OPTIONS`、`TRACE` 87 | // client参数用于替换DefaultClient,如果为nil则会使用默认的 88 | func Request(url, method string, client *http.Client) *Client { 89 | return newClient(url, method, client) 90 | } 91 | 92 | // Params http请求中url参数 93 | func (c *Client) Params(params url.Values) *Client { 94 | for k, v := range params { 95 | c.params[k] = v 96 | } 97 | return c 98 | } 99 | 100 | // Header http请求头 101 | func (c *Client) Header(k, v string) *Client { 102 | c.header.Set(k, v) 103 | return c 104 | } 105 | 106 | // Headers http请求头 107 | func (c *Client) Headers(header http.Header) *Client { 108 | for k, v := range header { 109 | c.header[k] = v 110 | } 111 | return c 112 | } 113 | 114 | // Form 表单提交参数 115 | func (c *Client) Form(form url.Values) *Client { 116 | c.header.Set(ContentType, ApplicationFormUrlencoded) 117 | c.form = form 118 | return c 119 | } 120 | 121 | // Json json提交参数 122 | // 如果是string,则默认当作是json字符串;否则会序列化为json字节数组,再发送 123 | func (c *Client) Json(json interface{}) *Client { 124 | c.header.Set(ContentType, ApplicationJSON) 125 | c.json = json 126 | return c 127 | } 128 | 129 | // Multipart form-data提交参数 130 | func (c *Client) Multipart(multipart FileForm) *Client { 131 | c.multipart = multipart 132 | return c 133 | } 134 | 135 | // Send 发送http请求 136 | func (c *Client) Send() *Result { 137 | var result *Result 138 | 139 | // 处理query string 140 | if c.params != nil && len(c.params) != 0 { 141 | // 如果url中已经有query string参数,则只需要&拼接剩下的即可 142 | encoded := c.params.Encode() 143 | if strings.Index(c.url, "?") == -1 { 144 | c.url += "?" + encoded 145 | } else { 146 | c.url += "&" + encoded 147 | } 148 | } 149 | 150 | // 根据不同的Content-Type设置不同的http body 151 | contentType := c.header.Get(ContentType) 152 | if c.multipart.Value != nil || c.multipart.File != nil { 153 | result = c.createMultipartForm() 154 | } else if strings.HasPrefix(contentType, ApplicationJSON) { 155 | result = c.createJson() 156 | } else if strings.HasPrefix(contentType, ApplicationFormUrlencoded) { 157 | result = c.createForm() 158 | } else { 159 | // 不是以上类型,就不设置http body 160 | result = c.createEmptyBody() 161 | } 162 | 163 | return result 164 | } 165 | 166 | // createMultipartForm 创建form-data的请求 167 | func (c *Client) createMultipartForm() *Result { 168 | var result = new(Result) 169 | 170 | body := &bytes.Buffer{} 171 | writer := multipart.NewWriter(body) 172 | 173 | // 设置文件字节 174 | for name, filename := range c.multipart.File { 175 | file, err := os.Open(filename) 176 | if err != nil { 177 | result.Err = err 178 | return result 179 | } 180 | 181 | part, err := writer.CreateFormFile(name, filename) 182 | if err != nil { 183 | result.Err = err 184 | return result 185 | } 186 | 187 | // todo 这里的io.Copy实现,会把file文件都读取到内存里面,然后当做一个buffer传给NewRequest。对于大文件来说会占用很多内存 188 | _, err = io.Copy(part, file) 189 | if err != nil { 190 | result.Err = err 191 | return result 192 | } 193 | 194 | err = file.Close() 195 | if err != nil { 196 | result.Err = err 197 | return result 198 | } 199 | } 200 | 201 | // 设置field 202 | for name, values := range c.multipart.Value { 203 | for _, value := range values { 204 | _ = writer.WriteField(name, value) 205 | } 206 | } 207 | 208 | err := writer.Close() 209 | if err != nil { 210 | result.Err = err 211 | return result 212 | } 213 | 214 | req, err := http.NewRequest(c.method, c.url, body) 215 | req.Header = c.header 216 | req.Header.Set(ContentType, writer.FormDataContentType()) 217 | c.doSend(req, result) 218 | return result 219 | } 220 | 221 | // createForm 创建application/json请求 222 | func (c *Client) createJson() *Result { 223 | var result = new(Result) 224 | 225 | b, err := json.Marshal(c.json) 226 | if err != nil { 227 | result.Err = err 228 | return result 229 | } 230 | 231 | req, err := http.NewRequest(c.method, c.url, bytes.NewReader(b)) 232 | if err != nil { 233 | result.Err = err 234 | return result 235 | } 236 | 237 | req.Header = c.header 238 | c.doSend(req, result) 239 | return result 240 | } 241 | 242 | // createForm 创建application/x-www-form-urlencoded请求 243 | func (c *Client) createForm() *Result { 244 | var result = new(Result) 245 | 246 | form := c.form.Encode() 247 | 248 | req, err := http.NewRequest(c.method, c.url, strings.NewReader(form)) 249 | if err != nil { 250 | result.Err = err 251 | return result 252 | } 253 | 254 | req.Header = c.header 255 | c.doSend(req, result) 256 | return result 257 | } 258 | 259 | // createEmptyBody 没有内容的body 260 | func (c *Client) createEmptyBody() *Result { 261 | var result = new(Result) 262 | 263 | req, err := http.NewRequest(c.method, c.url, nil) 264 | if err != nil { 265 | result.Err = err 266 | return result 267 | } 268 | 269 | req.Header = c.header 270 | c.doSend(req, result) 271 | return result 272 | } 273 | 274 | // doSend 发送请求 275 | func (c *Client) doSend(req *http.Request, result *Result) { 276 | // 调用拦截器,遇到错误就退出 277 | if err := c.beforeSend(req); err != nil { 278 | result.Err = err 279 | return 280 | } 281 | 282 | // 发送请求 283 | result.Resp, result.Err = c.client.Do(req) 284 | } 285 | 286 | // beforeSend 发送请求前,调用拦截器 287 | func (c *Client) beforeSend(req *http.Request) error { 288 | mutex := defaultRequestInterceptorChain.mutex 289 | mutex.RLock() 290 | defer mutex.RUnlock() 291 | 292 | // 遍历调用拦截器 293 | for _, interceptor := range defaultRequestInterceptorChain.interceptors { 294 | err := interceptor(req) 295 | if err != nil { 296 | return err 297 | } 298 | } 299 | return nil 300 | } 301 | 302 | // StatusOk 判断http响应码是否为200 303 | func (r *Result) StatusOk() *Result { 304 | if r.Err != nil { 305 | return r 306 | } 307 | if r.Resp.StatusCode != http.StatusOK { 308 | r.Err = errors.New("status code is not 200") 309 | return r 310 | } 311 | 312 | return r 313 | } 314 | 315 | // Status2xx 判断http响应码是否为2xx 316 | func (r *Result) Status2xx() *Result { 317 | if r.Err != nil { 318 | return r 319 | } 320 | if r.Resp.StatusCode < http.StatusOK || r.Resp.StatusCode >= http.StatusMultipleChoices { 321 | r.Err = errors.New("status code is not match [200, 300)") 322 | return r 323 | } 324 | 325 | return r 326 | } 327 | 328 | // Raw 获取http响应内容,返回字节数组 329 | func (r *Result) Raw() ([]byte, error) { 330 | if r.Err != nil { 331 | return nil, r.Err 332 | } 333 | 334 | b, err := ioutil.ReadAll(r.Resp.Body) 335 | if err != nil { 336 | r.Err = err 337 | return nil, r.Err 338 | } 339 | defer r.Resp.Body.Close() 340 | 341 | return b, r.Err 342 | } 343 | 344 | // Text 获取http响应内容,返回字符串 345 | func (r *Result) Text() (string, error) { 346 | b, err := r.Raw() 347 | if err != nil { 348 | r.Err = err 349 | return "", r.Err 350 | } 351 | 352 | return string(b), nil 353 | } 354 | 355 | // Json 获取http响应内容,返回json 356 | func (r *Result) Json(v interface{}) error { 357 | b, err := r.Raw() 358 | if err != nil { 359 | r.Err = err 360 | return r.Err 361 | } 362 | 363 | return json.Unmarshal(b, v) 364 | } 365 | 366 | // Save 获取http响应内容,保存为文件 367 | func (r *Result) Save(name string) error { 368 | if r.Err != nil { 369 | return r.Err 370 | } 371 | 372 | f, err := os.Create(name) 373 | if err != nil { 374 | r.Err = err 375 | return r.Err 376 | } 377 | defer f.Close() 378 | 379 | _, err = io.Copy(f, r.Resp.Body) 380 | if err != nil { 381 | r.Err = err 382 | return r.Err 383 | } 384 | defer r.Resp.Body.Close() 385 | 386 | return nil 387 | } 388 | 389 | // newClient 创建Client 390 | func newClient(u string, method string, client *http.Client) *Client { 391 | // client为nil则使用默认的DefaultClient 392 | if client == nil { 393 | client = http.DefaultClient 394 | } 395 | return &Client{ 396 | client: client, 397 | url: u, 398 | method: method, 399 | header: make(http.Header), 400 | params: make(url.Values), 401 | form: make(url.Values), 402 | } 403 | } 404 | 405 | // AddRequestInterceptors 添加请求拦截器 406 | func AddRequestInterceptors(interceptors ...RequestInterceptor) { 407 | mutex := defaultRequestInterceptorChain.mutex 408 | mutex.Lock() 409 | defer mutex.Unlock() 410 | 411 | // 添加到拦截器链 412 | for _, interceptor := range interceptors { 413 | defaultRequestInterceptorChain.interceptors = append(defaultRequestInterceptorChain.interceptors, interceptor) 414 | } 415 | } 416 | --------------------------------------------------------------------------------