# 36 HTTP客户端

标准库中的`net/http`包提供了发起HTTP请求的功能。

在示例中，我们使用[httpbin.org](https://httpbin.org)，它是一个聪明的HTTP服务，可以返回指定的HTTP响应，它用于演示HTTP协议的各个方面非常便利。

## 基本的GET请求

`http.Get()`只是`http.DefaultClient`的封装，后者是`*http.Client`类型的变量打包。

最好不要使用`http.Get`，因为它默认是没有超时的，这意味着它在连接到慢的或有缺陷的或恶意的服务上时永远等待。

{% hint style="danger" %}
**译者注**：对于网络服务来说，慢比错更可怕。慢意味着不确定性，如果不设置合适的超时限制，服务将被拖垮。
{% endhint %}

```go
package main

import (
	"fmt"
	"log"
	"net/http"
	"time"
)

func main() {
	client := &http.Client{}
	client.Timeout = time.Millisecond * 100

	uri := "https://httpbin.org/delay/3"
	resp, err := client.Get(uri)
	if err != nil {
		log.Fatalf("http.Get() failed with '%s'\n", err)
	}
	fmt.Println(resp)
}
```

如上所示，创建和使用自定义的`http.Client`是很容易的。

在上例中，我们设置了非常短的超时限制，以此演示因超时而取消连接。

在实际编程中，应该采用更长的超时时间，比如15秒（确切的超时时长应视情况而定）。

## 基本的POST请求

{% tabs %}
{% tab title="Go" %}

```go
package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"time"
)

func main() {
	client := &http.Client{}
	client.Timeout = time.Second * 15

	uri := "https://httpbin.org/post"
	body := bytes.NewBufferString("text we send")
	resp, err := client.Post(uri, "text/plain", body)
	if err != nil {
		log.Fatalf("client.Post() failed with '%s'\n", err)
	}
	defer resp.Body.Close()
	d, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatalf("http.Get() failed with '%s'\n", err)
	}
	fmt.Printf("http.Post() returned statuts code %d, truncated text:\n%s...\n", resp.StatusCode, string(d)[:93])
}

```

{% endtab %}

{% tab title="Output" %}

```
http.Post() returned statuts code 200, truncated text:
{
  "args": {}, 
  "data": "text we send", 
  "files": {}, 
  "form": {}, 
  "headers": {
   ...

```

{% endtab %}
{% endtabs %}

发起POST最简单的方法是使用`http.Client.Post(url string, contentType string, body io.Reader)`。

在上例中，我们发送了原始文本。在大多数情况下，服务端期望请求体是URL编码(Url-Encoded)后的格式。

## 基本的HEAD请求

跟GET用法一样，只改一下方法名`http.Client.Head(uri string)`。
