Go语言发送http请求常见用法

1. 简介

在日常工作中经常使用Go语言发送http请求,特此编写此文档来总结常见用法,以供日后参考。

使用net/http包来发送http请求,请参考 net/http文档 。

本文只是为了总结常见用法,仅介绍GETPOST两种http方法,其余方法参考这两种方法的用法即可。支持的方法有:

const (
    MethodGet     = "GET"
    MethodHead    = "HEAD"
    MethodPost    = "POST"
    MethodPut     = "PUT"
    MethodPatch   = "PATCH" // RFC 5789
    MethodDelete  = "DELETE"
    MethodConnect = "CONNECT"
    MethodOptions = "OPTIONS"
    MethodTrace   = "TRACE"
)

2. 实现最简单的GETPOST请求

当你仅需快速实现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))
}

3. 使用默认客户端

当你需要自定义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))
}

4. 使用自定义客户端

当你需要控制重定向策略、代理、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))
}

5. 在url中添加查询参数

当你需要在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))
}

6. 添加请求头

当你需要自定义http请求头时,可以通过RequestHeader成员的SetAdd方法来添加。

例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))
}

7. 上传文件

当你需要上传文件时,可以通过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
}

8. 下载文件

通过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"
}

你可能感兴趣的:(Go,语言,golang,http)