36.4 client

HTTP Client客户端主要用来发送HTTP Request请求给HTTP Server服务端,比如以Do方法,Get方法以及Post或PostForm方法发送HTTP Request请求。
// Client具有Do,Get,Head,Post以及PostForm等方法。 其中Do方法可以对
// Request进行一系列的设定,而其他的对request设定较少。如果Client使用默认的
// Client,则其中的Get,Head,Post以及PostForm方法相当于默认的http.Get, 
// http.Post, http.Head以及http.PostForm函数。
type Client struct
 
// 利用GET方法对一个指定的URL进行请求,如果response是如下重定向中的一个
// 代码,则Get之后将会调用重定向内容,最多10次重定向。 
// 301 (永久重定向,告诉客户端以后应该从新地址访问) 
// 302 (暂时性重定向,作为HTTP1.0的标准,PHP的默认Location重定向用到
// 也是302),注:303和307其实是对302的细化。 
// 303 (对于Post请求,它表示请求已经被处理,客户端可以接着使用GET方法去
// 请求Location里的URl) 
// 307 (临时重定向,对于Post请求,表示请求还没有被处理,客户端应该向
// Location里的URL重新发起Post请求)
func Get(url string) (resp *Response, err error) 
// 该函数功能见net中Head方法功能。该方法与默认的defaultClient中
// Head方法一致。
func Head(url string) (resp *Response, err error) 
// 该方法与默认的defaultClient中Post方法一致。
func Post(url string, bodyType string, body io.Reader) (resp *Response, err error)
 
// 该方法与默认的defaultClient中PostForm方法一致。 
func PostForm(url string, data url.Values) (resp *Response, err error)
// Do发送http请求并且返回一个http响应, 遵守client的策略, 如重定向, 
// cookies以及auth等.错误经常是由于策略引起的, 当err是nil时, resp
// 总会包含一个非nil的resp.body.当调用者读完resp.body之后应该关闭它, 
// 如果resp.body没有关闭, 则Client底层RoundTripper将无法重用存在的
// TCP连接去服务接下来的请求, 如果resp.body非nil, 则必须对其进行关闭.
// 通常来说, 经常使用Get, Post, 或者PostForm来替代Do. 
func (c *Client) Do(req *Request) (resp *Response, err error)
// 利用get方法请求指定的url.Get请求指定的页面信息,并返回实体主体。
func (c *Client) Get(url string) (resp *Response, err error) 
// 利用head方法请求指定的url,Head只返回页面的首部。
func (c *Client) Head(url string) (resp *Response, err error) 
// post方法请求指定的URl, 如果body也是一个io.Closer, 则在请求之后关闭它 
func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Response, err error)
// 利用post方法请求指定的url, 利用data的key和value作为请求体. 
func (c *Client) PostForm(url string, data url.Values) (resp *Response, err error)

http.NewRequest可以灵活的对http Request进行配置,然后再使用http.Client的Do方法发送这个http Request请求。注意:如果使用Post或者PostForm方法,是不能使用http.NewRequest配置请求的,只有Do方法可以定制http.NewRequest。

利用http.Client以及http.NewRequest就可以完整模拟一个HTTP Request请求,包括自定义的HTTP Request请求的头部信息。有了前面介绍的 HTTP Request 请求、HTTP Response 响应、HTTP Client 客户端 三个部分,我们已经可以模拟各种HTTP Request 请求的发送,接收HTTP Response 响应了。

下面我们来模拟http Request请求,请求中附带有cookie信息,通过http.Client的Do方法发送这个请求。

先配置http.NewRequest,然后我们通过http.Client的Do方法来发送任何HTTP Request请求。示例如下:

模拟任何HTTP Request请求:

package main
import (
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
)
func main() {
	// 简式声明一个http.Client空结构体指针对象
	client := &http.Client{}
	// 使用http.NewRequest构建http Request请求
	request, err := http.NewRequest("GET", "http://www.baidu.com", nil)
	if err != nil {
		fmt.Println(err)
	}
	// 使用http.Cookie结构体初始化一个cookie键值对
	cookie := &http.Cookie{Name: "userId", Value: strconv.Itoa(12345)}
	// 使用前面构建的request方法AddCookie往请求中添加cookie
	request.AddCookie(cookie)
	// 设置request的Header,具体可参考http协议
	request.Header.Set("Accept", "text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8")
	request.Header.Set("Accept-Charset", "GBK, utf-8;q=0.7, *;q=0.3")
	request.Header.Set("Accept-Encoding", "gzip, deflate, sdch")
	request.Header.Set("Accept-Language", "zh-CN, zh;q=0.8")
	request.Header.Set("Cache-Control", "max-age=0")
	request.Header.Set("Connection", "keep-alive")
	// 使用http.Client 来发送request,这里使用了Do方法。
	response, err := client.Do(request)
	if err != nil {
		fmt.Println(err)
		return
	}
	// 程序结束时关闭response.Body响应流
	defer response.Body.Close()
	// 接收到的http Response 状态值
	fmt.Println(response.StatusCode)
	if response.StatusCode == 200 { // 200意味成功得到http Server返回的http Response信息
		// gzip.NewReader对压缩的返回信息解压(考虑网络传输量,http Server
	// 一般都会对响应压缩后再返回)
		body, err := gzip.NewReader(response.Body)
		if err != nil {
			fmt.Println(err)
		}
		defer body.Close()
		r, err := ioutil.ReadAll(body)
		if err != nil {
			fmt.Println(err)
		}
		// 打印出http Server返回的http Response信息
		fmt.Println(string(r))
	}
}

使用http.Get 发送HTTP Get请求非常简单,在一般简单不需要对http.Request配置的场景下我们可以使用,只需要提供URL即可。

发送一个HTTP Get请求:

package main
import (
	"fmt"
	"io/ioutil"
	"net/http"
)
func main() {
	// var DefaultClient = &Client{}
	// func Get(url string) (resp *Response, err error) {
	// return DefaultClient.Get(url)
	// }
	/*
		func (c *Client) Get(url string) (resp *Response, err error) {
			req, err := NewRequest("GET", url, nil)
			if err != nil {
				return nil, err
			}
			return c.Do(req)
		}
	*/
	// http.Get实际上是DefaultClient.Get(url),Get函数是高度封装的,只有一个参数url。
	// 对于一般的http Request是可以使用,但是不能定制Request
	response, err := http.Get("http://www.baidu.com")
	if err != nil {
		fmt.Println(err)
	}
	//程序在使用完回复后必须关闭回复的主体。
	defer response.Body.Close()
	body, _ := ioutil.ReadAll(response.Body)
	fmt.Println(string(body))
}

使用http.Post 发送HTTP Post请求也非常简单,在一般简单不需要对http.Request配置的场景下我们可以使用。

发送一个http.Post请求:

package main
import (
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
)
func main() {
	// application/x-www-form-urlencoded:为POST的contentType
	// strings.NewReader("mobile=xxxxxxxxxx&isRemberPwd=1") 理解为传递的参数
	resp, err := http.Post("http://localhost:8080/login.do",
		"application/x-www-form-urlencoded", strings.NewReader("mobile=xxxxxxxxxx&isRemberPwd=1"))
	if err != nil {
		fmt.Println(err)
		return
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(string(body))
}

使用http.PostForm 发送HTTP Request请求也非常简单,而且可以附带参数的键值对作为请求的body传递到服务端。

发送一个http.PostForm请求:

package main
import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
)
func main() {
	postParam := url.Values{
		"mobile":      {"xxxxxx"},
		"isRemberPwd": {"1"},
	}
	// 数据的键值会经过URL编码后作为请求的body传递
	resp, err := http.PostForm("http://localhost:8080/login.do", postParam)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(string(body))
}

上面列举了四种HTTP Client客户端发送HTTP Request请求的方式,其中只有Do方法最灵活。

http.Client与http.NewRequest结合可以模拟任何HTTP Request请求,方法是Do。像Get方法,Post方法和PostForm方法,http.NewRequest都是定制好的,所以使用方便但灵活性不够。不过好在有Do方法,我们可以更灵活来配置http.NewRequest。

func NewRequest(method, url string, body io.Reader) (*Request, error)
func (c *Client) Get(url string) (resp *Response, err error) {
	req, err := NewRequest("GET", url, nil)
......
func (c *Client) Post(url string, contentType string, body io.Reader) (resp *Response, err error) {
	req, err := NewRequest("POST", url, body)
......
下一节:HTTP Server服务端用来接收并响应HTTP Client端发出的HTTP Request请求,是net/http包中非常重要和关键的一个功能。我们在Go语言中简单就能搭建HTTP服务器,就是因为它的存在。