• [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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

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

    http.Client

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

    httpClient = &http.Client{
    	Timeout: 5 * time.Second,
    }
    
    • 1
    • 2
    • 3

    请求示例

    返回应答中的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))
    
    • 1
    • 2
    • 3
    • 4

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

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

    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)
    
    • 1
    • 2

    通过PostForm发送:

    import (
        "net/http"
        "net/url"
    )
    
    payload := url.Values{"key": {"MyValue"}, "id": {"123"}}
    response, err := http.PostForm(targetUrl, payload)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    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
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    DELETE

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

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

    错误

    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)
        //...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这是因为:第一次发送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)
        //...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 相关阅读:
    maven-mvnd使用步骤,新版Maven,构建速度大幅提升
    spring cloud 快速上手系列 -> 01-注册中心 Eureka -> 012-Eureka客户端2
    vue 插槽
    Spring Bean细节
    Flutter 如何启动新的页面时给页面传递参数
    免费开源软件,医院能用吗?
    【手写Mybatis】step01:创建简单的代理工厂
    java代码性能优化总结 代码优化细节 java简单优化性能代码
    NISP网络信息安全面试怎么选择(六)NISP管理中心
    挑战杯 基于深度学习的人脸性别年龄识别 - 图像识别 opencv
  • 原文地址:https://blog.csdn.net/alwaysrun/article/details/126442786