在日常工作中经常使用Go语言发送http请求,特此编写此文档来总结常见用法,以供日后参考。
使用net/http
包来发送http请求,请参考 net/http文档 。
本文只是为了总结常见用法,仅介绍GET
和POST
两种http方法,其余方法参考这两种方法的用法即可。支持的方法有:
const (
MethodGet = "GET"
MethodHead = "HEAD"
MethodPost = "POST"
MethodPut = "PUT"
MethodPatch = "PATCH" // RFC 5789
MethodDelete = "DELETE"
MethodConnect = "CONNECT"
MethodOptions = "OPTIONS"
MethodTrace = "TRACE"
)
GET
和POST
请求当你仅需快速实现http请求,无需任何自定义配置时,可以直接使用http
包中函数来实现。
例1,使用http.Get
函数实现GET
请求:
func main() {
resp, err := http.Get("http://localhost:8080/ping")
if err != nil {
fmt.Println("http get failed:", err)
return
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("read response body failed:", err)
return
}
fmt.Println("http get succeed:", string(respBody))
}
例2、使用http.Post
函数实现POST
请求:
func main() {
body := bytes.NewReader([]byte(`{"name": "Jack", "sex": "male", "age": 18}`))
resp, err := http.Post("http://localhost:8080/user", "application/json", body)
if err != nil {
fmt.Println("http post failed:", err)
return
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("read response body failed:", err)
return
}
fmt.Println("http post succeed:", string(respBody))
}
当你需要自定义http请求而无需自定义客户端时,可以使用http
包提供的默认客户端来实现。
例1,为GET
请求实现超时控制:
func main() {
ctx, cancelFunc := context.WithTimeout(context.Background(), 5*time.Second)
defer cancelFunc()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://localhost:8080/ping", nil)
if err != nil {
fmt.Println("new http get request failed:", err)
return
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Println("http get failed:", err)
return
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("read response body failed:", err)
return
}
fmt.Println("http get succeed:", string(respBody))
}
例2,为POST
请求实现超时控制:
func main() {
body := bytes.NewReader([]byte(`{"name": "Jack", "sex": "male", "age": 18}`))
ctx, cancelFunc := context.WithTimeout(context.Background(), 5*time.Second)
defer cancelFunc()
req, err := http.NewRequestWithContext(ctx, http.MethodPost, "http://localhost:8080/user", body)
if err != nil {
fmt.Println("new http post request failed:", err)
return
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Println("http post failed:", err)
return
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("read response body failed:", err)
return
}
fmt.Println("http post succeed:", string(respBody))
}
当你需要控制重定向策略、代理、TLS配置、持久连接、压缩等配置时,你需要创建一个自定义客户端。
例1,为GET
请求指定重定向策略:
func main() {
client := &http.Client{
CheckRedirect: redirectPolicyFunc,
}
req, err := http.NewRequest(http.MethodGet, "http://localhost:8080/ping", nil)
if err != nil {
fmt.Println("new http get request failed:", err)
return
}
resp, err := client.Do(req)
if err != nil {
fmt.Println("http get failed:", err)
return
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("read response body failed:", err)
return
}
fmt.Println("http get succeed:", string(respBody))
}
func redirectPolicyFunc(req *http.Request, via []*http.Request) error {
return nil
}
例2,为POST
请求指定持久连接配置:
func main() {
tr := &http.Transport{
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
}
client := &http.Client{Transport: tr}
body := bytes.NewReader([]byte(`{"name": "Jack", "sex": "male", "age": 18}`))
req, err := http.NewRequest(http.MethodPost, "http://localhost:8080/user", body)
if err != nil {
fmt.Println("new http post request failed:", err)
return
}
resp, err := client.Do(req)
if err != nil {
fmt.Println("http post failed:", err)
return
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("read response body failed:", err)
return
}
fmt.Println("http post succeed:", string(respBody))
}
当你需要在url中添加查询参数时,可以使用url.Values
来添加。
例1,为GET
请求在url中添加查询参数:
func main() {
req, err := http.NewRequest(http.MethodGet, "http://localhost:8080/user", nil)
if err != nil {
fmt.Println("new http get request failed:", err)
return
}
params := url.Values{}
params.Add("name", "Jack")
req.URL.RawQuery = params.Encode()
resp, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Println("http get failed:", err)
return
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("read response body failed:", err)
return
}
fmt.Println("http get succeed:", string(respBody))
}
当你需要自定义http请求头时,可以通过Request
的Header
成员的Set
或Add
方法来添加。
例1,为POST
请求添加请求头:
func main() {
body := bytes.NewReader([]byte(`{"name": "Jack", "sex": "male", "age": 18}`))
req, err := http.NewRequest(http.MethodPost, "http://localhost:8080/user", body)
if err != nil {
fmt.Println("new http post request failed:", err)
return
}
req.Header.Set("Content-Type", "application/json")
req.Header.Add("Accept-Language", "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7")
resp, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Println("http post failed:", err)
return
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("read response body failed:", err)
return
}
fmt.Println("http post succeed:", string(respBody))
}
当你需要上传文件时,可以通过multipart.Writer
来实现。
例1,使用multipart.Writer
实现上传文件功能:
func main() {
filePath := "go.mod"
multipartWriter, reqBody, err := newUploadFile(filePath)
if err != nil {
fmt.Println(err)
return
}
req, err := http.NewRequest(http.MethodPost, "http://localhost:8080/file", reqBody)
if err != nil {
fmt.Println("create request failed:", err)
return
}
req.Header.Set("Content-Type", multipartWriter.FormDataContentType())
resp, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Println("send request failed:", err)
return
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("read response failed:", err)
return
}
fmt.Println("upload file succeed:", string(respBody))
}
func newUploadFile(filePath string) (*multipart.Writer, *bytes.Buffer, error) {
var reqBody bytes.Buffer
multipartWriter := multipart.NewWriter(&reqBody)
part, err := multipartWriter.CreateFormFile("upload", filepath.Base(filePath))
if err != nil {
return nil, nil, fmt.Errorf("create form file failed: %v", err)
}
file, err := os.Open(filePath)
if err != nil {
return nil, nil, fmt.Errorf("open file failed: %v", err)
}
defer file.Close()
_, err = io.Copy(part, file)
if err != nil {
return nil, nil, fmt.Errorf("copy file content failed: %v", err)
}
err = multipartWriter.Close()
if err != nil {
return nil, nil, fmt.Errorf("close multipart writer failed: %v", err)
}
return multipartWriter, &reqBody, nil
}
通过http接口下载文件的实现特别简单,将响应体数据写入本地文件即可。
例1,下载文件到download
目录下,从响应头中的Content-Disposition
字段提取文件名:
func main() {
resp, err := http.Get("http://localhost:8080/file")
if err != nil {
fmt.Println("http get failed:", err)
return
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("read response body failed:", err)
return
}
filename := filenameFromRespHeader(resp.Header)
err = os.WriteFile(path.Join("download", filename), respBody, os.ModePerm)
if err != nil {
return
}
fmt.Println("http get succeed:", string(respBody))
}
func filenameFromRespHeader(header http.Header) string {
r := regexp.MustCompile(`^.*filename="(.*)"`)
s := header.Get("Content-Disposition")
ss := r.FindStringSubmatch(s)
if len(ss) >= 2 {
return ss[1]
}
return "unknown"
}