[go]HTTP之RestAPI请求


go内置的net/http包,提供了http相关操作的实现。

HTTP协议

作为文本传输协议,HTTP的协议头是文本数据,HTTP请求头的首行会包含请求的方法、路径和协议版本,接下来是多个HTTP协议头以及携带的负载。

go的HTTP客户端包含以下比较重要的结构体:

  • net/http.Client:HTTP客户端;是级别较高的抽象,它提供了HTTP的一些细节,包括Cookies和重定向;
  • net/http.Transport:处理HTTP/HTTPS协议的底层实现细节,其中会包含连接重用、构建请求以及发送请求等功能;
  • net/http.persistConn:封装了一个TCP的持久连接,是与远程交换消息的句柄(Handle);

请求

net/http.Request是HTTP的请求,其中包含HTTP请求的方法、URL、协议版本、协议头以及请求体等字段。

type Request struct {
    Method string
    URL *url.URL

    Proto      string // "HTTP/1.0"
    ProtoMajor int    // 1
    ProtoMinor int    // 0

    Header Header
    Body io.ReadCloser

    ...
    Response *Response
}

库中提供了http.NewRequest用于创建请求的方法,会校验HTTP请求的字段并根据输入的参数拼装成新的请求结构体。

http.Client

库中提供了一个http.DefaultClient,可用作缺省的客户端;此客户端没有超时处理,若服务端无返回,会一直挂在那儿,可通过设定超时时间,来避免此情形:

httpClient = &http.Client{
	Timeout: 5 * time.Second,
}

请求示例

返回应答中的body在用完时要及时关闭:
defer resp.Body.Close()

GET

若没有其他额外参数,可直接通过Get方式获取:

  response,_ := http.Get("http://www.baidu.com")
  defer response.Body.Close()
  body,_ := ioutil.ReadAll(response.Body)
  fmt.Println(string(body))

若要添加参数,如添加token头信息,则需要先构造请求:

  req, _ := http.NewRequest("GET", targetUrl, nil)
  req.Header.Add("token", "xxxx")
  response, err := httpClient.Do(req)
  // ...

POST

POST请求通常有两种形式:

  • kv形式:如form-datax-www-form-urlencoded
  • json形式:如application/json
KV形式

通过Post直接发送KV键值对:

payload := strings.NewReader("key=myValue")
response, err := http.Post(targetUrl, "application/x-www-form-urlencoded", payload)

通过PostForm发送:

import (
    "net/http"
    "net/url"
)

payload := url.Values{"key": {"MyValue"}, "id": {"123"}}
response, err := http.PostForm(targetUrl, payload)
JSON形式
body := `{"seq":"xxxxx","user":"` + user + `","psw":"` + psw + `"}`
req, err := http.NewRequest("POST", targetUrl, bytes.NewBuffer([]byte(body)))
if err != nil {
	log.Println("[ERROR] NewRequest of token fail:", err)
	return err
}
req.Header.Set("Content-Type", "application/json")
resp, err := httpClient.Do(req)
if err != nil {
	log.Println("[ERROR] token request fail:", err)
	return err
}
defer resp.Body.Close()

respBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
	log.Println("[ERROR] read token response body fail:", err)
	return err
}
var result map[string]interface{}
err = json.Unmarshal(respBody, &result)
if err != nil {
	log.Println("[ERROR] Unmarshal token response fail:", err)
	return err
}

if v, ok := result["token"]; ok {
	if token, got := v.(string); got {
		log.Printf("[DEBUG] token got: %v", token)
		return nil
	}
}

DELETE

http中没有直接提供delete方法,只能通过request构造:

req, _ := http.NewRequest("DELETE", targetUrl, nil)
req.Header.Add("token", "xxxx")
response, err := httpClient.Do(req)

错误

HTTP请求在重用Request结构体可能会出现以下错误:
http: ContentLength=XXX with Body length 0

//req定义在for循坏外
req, err := http.NewRequest("POST", url, bytes.NewReader(data))
 
// 多次请求
for {
   resp, err := httpClient.Do(req)
    //...
}

这是因为:第一次发送http请求失败后,后续尝试请求时req的body被清空了;body是个io.Reader,第一次发送后,Request.Body已经被读并且stream被关闭了,后续请求再读取就为空了。

所以req定义要与Do请求在一起:

for {
   req, err := http.NewRequest("POST", url, bytes.NewReader(data))
   resp, err := httpClient.Do(req)
    //...
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值