Golang内置net/http
包中http.Client
结构用于实现HTTP客户端,因此无需借助第三方网络通信库(如libcurl
)就可以直接使用GET或POST方式发起HTTP请求,实现远程请求页面。
type Client struct {
Transport RoundTripper//HTTP请求创建机制,如果为空将会使用DefaultTransport。
CheckRedirect func(req *Request, via []*Request) error//重定向策略
Jar CookieJar //如果Jar为空,Cookie将不会在请求中发送,并会在响应中被忽略。
Timeout time.Duration
}
属性 | 描述 |
---|---|
Transport | HTTP事务用于处理客户端的请求并等待服务端的响应 |
CheckRedirect | 指定处理重定向的策略 |
Jar | 指定COOKIE的Jar |
Timeout | 指定客户端请求的最大超时时间 |
http.Client
被设计成上下两层结构
- 上层:业务层
上层封装的基础方法可称之为业务层,因为调用方通常只需要关心请求的业务逻辑本身,而无需关心非业务相关的技术细节。
- 下层:传输层
由于http.Client
在底层抽象了http.RoundTripper
接口,而http.Transport
实现了该接口,从而能够处理更多细节,可将其视为下层的“传输层”。
Transport
指定执行HTTP请求的运行机制,可用于设置HTTP底层传输细节、HTTP代理、GZIP压缩、连接池及其管理、SSL认证等。
http.Client
发送JSON数据
func HTTPJson(method string, url string, params []byte) ([]byte, error) {
body := bytes.NewBuffer(params)
req, err := http.NewRequest(method, url, body)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
if resp != nil {
defer resp.Body.Close()
}
if resp.StatusCode == http.StatusOK {
msg := fmt.Sprintf("response status code %v", resp.StatusCode)
return nil, errors.New(msg)
}
bs, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return bs, nil
}
client.Do
func (c *Client) Do(req *Request) (*Response, error) {
return c.do(req)
}
func HTTPDo(method string, url string, values url.Values) ([]byte, error) {
body := strings.NewReader(values.Encode())
req, err := http.NewRequest(method, url, body)
if err != nil {
return nil, err
}
//req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
//req.Header.Set("Cookie", cookie)
//req.Header.Set("Connection", "keep-alive")
//req.Header.Add("x-requested-with", "XMLHttpRequest") //AJAX
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
if resp != nil {
defer resp.Body.Close()
}
if resp.StatusCode == http.StatusOK {
msg := fmt.Sprintf("response status code %v", resp.StatusCode)
return nil, errors.New(msg)
}
bs, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return bs, nil
}
http.NewRequest
func NewRequest(method, url string, body io.Reader) (*Request, error) {
return NewRequestWithContext(context.Background(), method, url, body)
}
http.Get
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)
}
func HTTPGet(url string) ([]byte, error) {
//发送请求获取响应
resp, err := http.Get(url)
if err != nil {
return nil, err
}
//结束网络释放资源
if resp != nil {
defer resp.Body.Close()
}
//判断响应状态码
if resp.StatusCode != http.StatusOK {
return nil, errors.New(fmt.Sprintf("response status code %v", resp.StatusCode))
}
//读取响应实体
bs, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return bs, nil
}
通过http.Get
发起请求时,默认调用的是http.Client
默认对象上的Get
方法。
func Get(url string) (resp *Response, err error) {
return DefaultClient.Get(url)
}
DefaultClient
默认指向的正是http.Client
的实例对象。
var DefaultClient = &Client{}
http.Get()
方法返回值
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)
}
返回值 | 类型 | 描述 |
---|---|---|
resp | *http.Response | 响应对象 |
err | error | 错误对象 |
若请求过程中出现错误则error
对象不为空,否则可通过resp
响应对象获取响应码、响应头、响应实体等信息。
响应 | 描述 |
---|---|
resp.StatusCode | 获取响应状态码 |
resp.Header | 获取响应头 |
resp.Body | 获取响应实体 |
http.Post
func Post(url, contentType string, body io.Reader) (resp *Response, err error) {
return DefaultClient.Post(url, contentType, body)
}
- HTTP POST请求的参数需要通过
url.Values
进行编码和封装
func HTTPPost(url string, params url.Values, contentType string) ([]byte, error) {
body := strings.NewReader(params.Encode())
if contentType == "" {
contentType = "application/x-www-form-urlencoded"
}
resp, err := http.Post(url, contentType, body)
if err != nil {
return nil, err
}
if resp != nil {
defer resp.Body.Close()
}
if resp.StatusCode != http.StatusOK {
return nil, errors.New(fmt.Sprintf("response status code %v", resp.StatusCode))
}
bs, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return bs, nil
}
http.PostForm
func PostForm(url string, data url.Values) (resp *Response, err error) {
return DefaultClient.PostForm(url, data)
}
func (c *Client) PostForm(url string, data url.Values) (resp *Response, err error) {
return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
}
func HTTPPostForm(url string, values url.Values) ([]byte, error) {
resp, err := http.PostForm(url, values)
if err != nil {
return nil, err
}
if resp != nil {
defer resp.Body.Close()
}
if resp.StatusCode != http.StatusOK {
return nil, errors.New(fmt.Sprintf("response status code %v", resp.StatusCode))
}
bs, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return bs, nil
}
url.Values
转化参数为 id=1&name=admin&pid=0
格式
func MakeParams(params url.Values) string {
var keys []string
for k := range params {
keys = append(keys, k)
}
sort.Strings(keys)
bb := bytes.Buffer{}
for _, key := range keys {
val := params.Get(key)
bb.WriteString(key)
bb.WriteString("=")
bb.WriteString(val)
bb.WriteString("&")
}
return strings.TrimRight(bb.String(), "&")
}
$ vim ./test/ins_test.go
package test
import (
"gfw/web"
"net/url"
"testing"
)
func TestCurl(t *testing.T) {
params := url.Values{}
params.Add("id", "1")
params.Add("name", "admin")
params.Add("pid", "0")
body := web.MakeParams(params)
t.Log(body)
}
$ go test -v -run TestCurl ins_test.go
=== RUN TestCurl
ins_test.go:16: id=1&name=admin&pid=0
--- PASS: TestCurl (0.00s)
PASS
ok command-line-arguments 0.300s