【Go】——Golang处理HTTP/HTTPS请求

妖狐艹你老母 2023-01-19 03:10 105阅读 0赞

一、前言

  1. 绕不开的HTTP。。。。

二、HTTP知识点补充

1. http操作的方法

  1. HTTP定义了与服务器交互的不同方法,最基本的方法有4种,分别是GETPOSTPUTDELETEURL全称是资源描述符。我们可以这样认为: 一个URL地址,它用于描述一个网络上的资源,而HTTP中的GETPOSTPUTDELETE就对应着对这个资源的 查,改,增,删 4个操作。到这里,大家应该有个大概的了解了,GET一般用于获取/查询资源信息,而POST一般用于更新资源信息。那么,除了上面说的四种方法,HTTP还有其它方法么?其实HTTP中定义了以下几种请求方法:
  • GET方法;
  • POST方法;
  • PUT方法;
  • DELETE方法。
  • HEAD方法;
  • TRACE方法;
  • OPTIONS方法;

1.Get是最常用的方法,通常用于请求服务器发送某个资源,而且应该是安全的和幂等的。

  1. (1). 所谓安全是指该操作用于获取信息而非修改信息。换句话说,GET 请求一般不应产生副作用。就是说,它仅仅是获取资源信息,就像数据库查询一样,不会修改和增加数据,不会影响资源的状态。

  注意:这里安全的含义仅仅是指是非修改信息。

  1. (2). 幂等是指对同一个URL的多个请求应该返回同样的结果。
  1. POST方法向服务器提交数据,比如完成表单数据的提交,将数据提交给服务器处理。

  2. PUT方法是让服务器用请求的主体部分来创建一个由所请求的URL命名的新文档;如果那个文档存在的话,就用这个主体来代替它。

  3. DELETE方法就是请求服务器删除指定URL所对应的资源。但是,客户端无法保证删除操作一定会被执行,因为HTTP规范允许服务器在不通知客户端的情况下撤销请求。

  4. HEAD方法与GET方法的行为很类似,但服务器在响应中只返回实体的主体部分。这就允许客户端在未获取实际资源的情况下,对资源的首部进行检查,

使用HEAD,我们可以更高效的完成以下工作:
①. 在不获取资源的情况下,了解资源的一些信息,比如资源类型;
②. 通过查看响应中的状态码,可以确定资源是否存在;
③. 通过查看首部,测试资源是否被修改。

  1. TRACE方法会在目的服务器端发起一个“回环”诊断,我们都知道,客户端在发起一个请求时,这个请求可能要穿过防火墙、代理、网关、或者其它的一些应用程序。这中间的每个节点都可能会修改原始的HTTP请求,TRACE方法允许客户端在最终将请求发送服务器时,它变成了什么样子。由于有一个“回环”诊断,在请求最终到达服务器时,服务器会弹回一条TRACE响应,并在响应主体中携带它收到的原始请求报文的最终模样。这样客户端就可以查看HTTP请求报文在发送的途中,是否被修改过了。

  2. OPTIONS方法用于获取当前URL所支持的方法。若请求成功,则它会在HTTP头中包含一个名为“Allow”的头,值是所支持的方法,如“GET, POST”。

2.http状态码和含义






























状态码 含义
1xx 请求正被处理
2xx 请求成功处理
3xx 请求需要附加操作,常见的例子如重定向
4xx 客户端出错导致请求无法被处理
5xx 服务端处理出错

三、GET请求

  1. 基本的get请求

    //基本的GET请求
    package main

    import (

    1. "fmt"
    2. "io/ioutil"
    3. "net/http"

    )

    func main() {

    1. resp, err := http.Get("http://httpbin.org/get")
    2. if err != nil {
    3. fmt.Println(err)
    4. return
    5. }
    6. defer resp.Body.Close()
    7. body, err := ioutil.ReadAll(resp.Body)
    8. fmt.Println(string(body))
    9. fmt.Println(resp.StatusCode)
    10. if resp.StatusCode == 200 {
    11. fmt.Println("ok")
    12. }

    }

  2. 带参数的get请求

    package main

    import (

    1. "fmt"
    2. "io/ioutil"
    3. "net/http"

    )

    func main(){

    1. resp, err := http.Get("http://httpbin.org/get?name=zhangsan&age=23")
    2. if err != nil {
    3. fmt.Println(err)
    4. return
    5. }
    6. defer resp.Body.Close()
    7. body, _ := ioutil.ReadAll(resp.Body)
    8. fmt.Println(string(body))

    }

3.但是如果我们想要把一些参数做成变量而不是直接放到url中怎么操作,代码例子如下:

  1. package main
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "net/http"
  6. "net/url"
  7. )
  8. func main(){
  9. params := url.Values{}
  10. Url, err := url.Parse("http://httpbin.org/get")
  11. if err != nil {
  12. return
  13. }
  14. params.Set("name","zhangsan")
  15. params.Set("age","23")
  16. //如果参数中有中文参数,这个方法会进行URLEncode
  17. Url.RawQuery = params.Encode()
  18. urlPath := Url.String()
  19. fmt.Println(urlPath) // https://httpbin.org/get?age=23&name=zhangsan
  20. resp,err := http.Get(urlPath)
  21. defer resp.Body.Close()
  22. body, _ := ioutil.ReadAll(resp.Body)
  23. fmt.Println(string(body))
  24. }
  1. 解析JSON类型的返回结果

    package main

    import (

    1. "encoding/json"
    2. "fmt"
    3. "io/ioutil"
    4. "net/http"

    )

    type result struct {

    1. Args string `json:"args"`
    2. Headers map[string]string `json:"headers"`
    3. Origin string `json:"origin"`
    4. Url string `json:"url"`

    }

    func main() {

    1. resp, err := http.Get("http://httpbin.org/get")
    2. if err != nil {
    3. return
    4. }
    5. defer resp.Body.Close()
    6. body, _ := ioutil.ReadAll(resp.Body)
    7. fmt.Println(string(body))
    8. var res result
    9. _ = json.Unmarshal(body,&res)
    10. fmt.Printf("%#v", res)

    }

5.GET请求添加请求头

  1. package main
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "net/http"
  6. )
  7. func main() {
  8. client := &http.Client{}
  9. req,_ := http.NewRequest("GET","http://httpbin.org/get",nil)
  10. req.Header.Add("name","zhangsan")
  11. req.Header.Add("age","3")
  12. resp,_ := client.Do(req)
  13. body, _ := ioutil.ReadAll(resp.Body)
  14. fmt.Printf(string(body))
  15. }

四、POST请求

  1. 基本的post请求

    package main

    import (

    1. "fmt"
    2. "io/ioutil"
    3. "net/http"
    4. "net/url"

    )

    func main() {

    1. urlValues := url.Values{}
    2. urlValues.Add("name","zhangsan")
    3. urlValues.Add("age","22")
    4. resp, _ := http.PostForm("http://httpbin.org/post",urlValues)
    5. body, _ := ioutil.ReadAll(resp.Body)
    6. fmt.Println(string(body))

    }

结果如下:

  1. {
  2. "args": {},
  3. "data": "",
  4. "files": {},
  5. "form": {
  6. "age": "22",
  7. "name": "zhangsan"
  8. },
  9. "headers": {
  10. "Accept-Encoding": "gzip",
  11. "Content-Length": "19",
  12. "Content-Type": "application/x-www-form-urlencoded",
  13. "Host": "httpbin.org",
  14. "User-Agent": "Go-http-client/1.1"
  15. },
  16. "json": null,
  17. "origin": "211.138.20.170, 211.138.20.170",
  18. "url": "https://httpbin.org/post"
  19. }
  1. 另一种方式

    package main

    import (

    1. "fmt"
    2. "io/ioutil"
    3. "net/http"
    4. "net/url"
    5. "strings"

    )

    func main() {

    1. urlValues := url.Values{
    2. "name":{"zhangsan"},
    3. "age":{"23"},
    4. }
    5. reqBody:= urlValues.Encode()
    6. resp, _ := http.Post("http://httpbin.org/post", "text/html",strings.NewReader(reqBody))
    7. body,_:= ioutil.ReadAll(resp.Body)
    8. fmt.Println(string(body))

    }

结果

  1. {
  2. "args": {},
  3. "data": "age=23&name=zhangsan",
  4. "files": {},
  5. "form": {},
  6. "headers": {
  7. "Accept-Encoding": "gzip",
  8. "Content-Length": "19",
  9. "Content-Type": "text/html",
  10. "Host": "httpbin.org",
  11. "User-Agent": "Go-http-client/1.1"
  12. },
  13. "json": null,
  14. "origin": "211.138.20.170, 211.138.20.170",
  15. "url": "https://httpbin.org/post"
  16. }
  1. 发送JSON数据的post请求

    package main

    import (

    1. "bytes"
    2. "encoding/json"
    3. "fmt"
    4. "io/ioutil"
    5. "net/http"

    )

    func main() {

    1. client := &http.Client{}
    2. data := make(map[string]interface{})
    3. data["name"] = "zhangsan"
    4. data["age"] = "23"
    5. bytesData, _ := json.Marshal(data)
    6. req, _ := http.NewRequest("POST","http://httpbin.org/post",bytes.NewReader(bytesData))
    7. resp, _ := client.Do(req)
    8. body, _ := ioutil.ReadAll(resp.Body)
    9. fmt.Println(string(body))

    }

结果

  1. {
  2. "args": {},
  3. "data": "{\"age\":\"23\",\"name\":\"zhangsan\"}",
  4. "files": {},
  5. "form": {},
  6. "headers": {
  7. "Accept-Encoding": "gzip",
  8. "Content-Length": "29",
  9. "Host": "httpbin.org",
  10. "User-Agent": "Go-http-client/1.1"
  11. },
  12. "json": {
  13. "age": "23",
  14. "name": "zhangsan"
  15. },
  16. "origin": "211.138.20.170, 211.138.20.170",
  17. "url": "https://httpbin.org/post"
  18. }

五、HTTPS请求

  1. HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSLSecure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。简单来说,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。

  HTTPS和HTTP的区别主要如下:

  1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。

  2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。

  3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。

  4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

对于https的请求,我们是没有证书是拿不到想要的数据的。所以go代码层面就得添加进证书去。

  1. 添加证书的代码

    package xxxxxxxxxxxxxxxxxxxxxxxxxx

    import (

    1. "crypto/tls"
    2. "crypto/x509"
    3. "io/ioutil"
    4. "net/http"
    5. "time"

    )

    func GetHttps(url, caCertPath, certFile, keyFile string) ([]byte, error) {

    1. // 创建证书池及各类对象
    2. var pool *x509.CertPool // 我们要把一部分证书存到这个池中
    3. var client *http.Client
    4. var resp *http.Response
    5. var body []byte
    6. var err error
    7. var caCrt []byte // 根证书
    8. caCrt, err = ioutil.ReadFile(caCertPath)
    9. pool = x509.NewCertPool()
    10. if err != nil {
    11. return nil, err
    12. }
    13. pool.AppendCertsFromPEM(caCrt)
    14. var cliCrt tls.Certificate // 具体的证书加载对象
    15. cliCrt, err = tls.LoadX509KeyPair(certFile, keyFile)
    16. if err != nil {
    17. return nil, err
    18. }
    19. // 把上面的准备内容传入 client
    20. client = &http.Client{
    21. Transport: &http.Transport{
    22. TLSClientConfig: &tls.Config{
    23. RootCAs: pool,
    24. Certificates: []tls.Certificate{cliCrt},
    25. },
    26. },
    27. }
    28. // Get 请求
    29. resp, err = client.Get(url)
    30. if err != nil {
    31. return nil, err
    32. }
    33. defer resp.Body.Close()
    34. body, err = ioutil.ReadAll(resp.Body)
    35. if err != nil {
    36. return nil, err
    37. }
    38. defer client.CloseIdleConnections()
    39. return body, nil

    }

我们把服务器中的证书文件拿到本地,作为参数传入程序。

  1. func TestGetHttps(t *testing.T) {
  2. resp, err := GetHttps("https://xx.xx.xxx.xxx:xxxx/metrics",
  3. "C:/Users/Desktop/ca.crt",
  4. "C:/Users/Desktop/healthcheck-client.crt",
  5. "C:/UsersDesktop/healthcheck-client.key")
  6. if err != nil {
  7. fmt.Println(err)
  8. }
  9. fmt.Println(string(resp))
  10. }

这里使用了 “crypto” 标准库,其中,我们使用

  • tls.LoadX509KeyPair()方法读取证书路径,转换为证书对象;
  • x509.NewCertPool()方法创建证书池;
  • pool.AppendCertsFromPEM(caCrt)方法将根证书加入到证书池中。

2. 在 Header 中添加 token 的 HTTPS 请求

当我们添加完了证书之后,对于有些需要认证的网页,我们依然拿不到数据,怎么办,加认证。

  1. func GetHttpsSkip(url, token string) ([]byte, error) {
  2. // 创建各类对象
  3. var client *http.Client
  4. var request *http.Request
  5. var resp *http.Response
  6. var body []byte
  7. var err error
  8. `这里请注意,使用 InsecureSkipVerify: true 来跳过证书验证`
  9. client = &http.Client{Transport: &http.Transport{
  10. TLSClientConfig: &tls.Config{
  11. InsecureSkipVerify: true,
  12. },
  13. }}
  14. // 获取 request请求
  15. request, err = http.NewRequest("GET", url, nil)
  16. if err != nil {
  17. log.Println("GetHttpSkip Request Error:", err)
  18. return nil, nil
  19. }
  20. // 加入 token
  21. request.Header.Add("Authorization", token)
  22. resp, err = client.Do(request)
  23. if err != nil {
  24. log.Println("GetHttpSkip Response Error:", err)
  25. return nil, nil
  26. }
  27. defer resp.Body.Close()
  28. body, err = ioutil.ReadAll(resp.Body)
  29. defer client.CloseIdleConnections()
  30. return body, nil
  31. }

传入 token,验证

  1. func TestGetHttps(t *testing.T) {
  2. resp, err := GetHttpsSkip("https://10.10.102.91:10250/metrics",
  3. "Bearer eyxxxxxxxxxxxxxxxxxxxx....xxxxx")
  4. if err != nil {
  5. fmt.Println(err)
  6. }
  7. fmt.Println(string(resp))
  8. }

六、go常用的Http库

  1. https://github.com/parnurzeal/gorequest

  2. https://github.com/kirinlabs/HttpRequest

七、参考资料

  1. https://www.cnblogs.com/zhaof/p/11346412.html

发表评论

表情:
评论列表 (有 0 条评论,105人围观)

还没有评论,来说两句吧...

相关阅读