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

一、HTTP知识点补充

  1. http操作的方法

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

  • GET方法;
  • POST方法;
  • PUT方法;
  • DELETE方法。
  • HEAD方法;
  • TRACE方法;
  • OPTIONS方法;

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

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

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

(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”。

二、GET请求

  1. 基本的get请求
//基本的GET请求
package main
 
import (
    "fmt"
    "io/ioutil"
    "net/http"
)
 
func main() {
    resp, err := http.Get("http://httpbin.org/get")
    if err != nil {
        fmt.Println(err)
        return
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
    fmt.Println(resp.StatusCode)
    if resp.StatusCode == 200 {
        fmt.Println("ok")
    }
}
  1. 带参数的get请求
package main
 
import (
    "fmt"
    "io/ioutil"
    "net/http"
)
 
func main(){
    resp, err := http.Get("http://httpbin.org/get?name=zhangsan&age=23")
    if err != nil {
        fmt.Println(err)
        return
    }
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
 
}

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

package main
 
import (
    "fmt"
    "io/ioutil"
    "net/http"
    "net/url"
)
 
func main(){
    params := url.Values{}
    Url, err := url.Parse("http://httpbin.org/get")
    if err != nil {
        return
    }
    params.Set("name","zhangsan")
    params.Set("age","23")
    //如果参数中有中文参数,这个方法会进行URLEncode
    Url.RawQuery = params.Encode()
    urlPath := Url.String()
    fmt.Println(urlPath) // https://httpbin.org/get?age=23&name=zhangsan
    resp,err := http.Get(urlPath)
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}
  1. 解析JSON类型的返回结果
package main
 
import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
)
 
type result struct {
    Args string `json:"args"`
    Headers map[string]string `json:"headers"`
    Origin string `json:"origin"`
    Url string `json:"url"`
}
 
func main() {
    resp, err := http.Get("http://httpbin.org/get")
    if err != nil {
        return
    }
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
    var res result
    _ = json.Unmarshal(body,&res)
    fmt.Printf("%#v", res)
}

5.GET请求添加请求头

package main
 
import (
    "fmt"
    "io/ioutil"
    "net/http"
)
 
func main() {
    client := &http.Client{}
    req,_ := http.NewRequest("GET","http://httpbin.org/get",nil)
    req.Header.Add("name","zhangsan")
    req.Header.Add("age","3")
    resp,_ := client.Do(req)
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Printf(string(body))
}

三、POST请求

  1. 基本的post请求
package main
 
import (
    "fmt"
    "io/ioutil"
    "net/http"
    "net/url"
)
 
func main() {
    urlValues := url.Values{}
    urlValues.Add("name","zhangsan")
    urlValues.Add("age","22")
    resp, _ := http.PostForm("http://httpbin.org/post",urlValues)
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}

结果如下:

{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "age": "22", 
    "name": "zhangsan"
  }, 
  "headers": {
    "Accept-Encoding": "gzip", 
    "Content-Length": "19", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "httpbin.org", 
    "User-Agent": "Go-http-client/1.1"
  }, 
  "json": null, 
  "origin": "211.138.20.170, 211.138.20.170", 
  "url": "https://httpbin.org/post"
}
  1. 另一种方式
package main
 
import (
    "fmt"
    "io/ioutil"
    "net/http"
    "net/url"
    "strings"
)
 
func main() {
    urlValues := url.Values{
        "name":{"zhangsan"},
        "age":{"23"},
    }
    reqBody:= urlValues.Encode()
    resp, _ := http.Post("http://httpbin.org/post", "text/html",strings.NewReader(reqBody))
    body,_:= ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}

结果

{
  "args": {}, 
  "data": "age=23&name=zhangsan", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept-Encoding": "gzip", 
    "Content-Length": "19", 
    "Content-Type": "text/html", 
    "Host": "httpbin.org", 
    "User-Agent": "Go-http-client/1.1"
  }, 
  "json": null, 
  "origin": "211.138.20.170, 211.138.20.170", 
  "url": "https://httpbin.org/post"
}
  1. 发送JSON数据的post请求
package main
 
import (
    "bytes"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
)
 
func main() {
    client := &http.Client{}
    data := make(map[string]interface{})
    data["name"] = "zhangsan"
    data["age"] = "23"
    bytesData, _ := json.Marshal(data)
    req, _ := http.NewRequest("POST","http://httpbin.org/post",bytes.NewReader(bytesData))
    resp, _ := client.Do(req)
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
 
}

结果

{
  "args": {}, 
  "data": "{\"age\":\"23\",\"name\":\"zhangsan\"}", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept-Encoding": "gzip", 
    "Content-Length": "29", 
    "Host": "httpbin.org", 
    "User-Agent": "Go-http-client/1.1"
  }, 
  "json": {
    "age": "23", 
    "name": "zhangsan"
  }, 
  "origin": "211.138.20.170, 211.138.20.170", 
  "url": "https://httpbin.org/post"
}

四、HTTPS请求

HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure 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代码层面就得添加进证书去。

添加证书的代码

package xxxxxxxxxxxxxxxxxxxxxxxxxx
 
import (
	"crypto/tls"
	"crypto/x509"
	"io/ioutil"
	"net/http"
	"time"
)
 
func GetHttps(url, caCertPath, certFile, keyFile string) ([]byte, error) {
 
	// 创建证书池及各类对象
	var pool *x509.CertPool  // 我们要把一部分证书存到这个池中
	var client *http.Client
	var resp *http.Response
	var body []byte
	var err error
 
	var caCrt []byte  // 根证书
	caCrt, err = ioutil.ReadFile(caCertPath)
	pool = x509.NewCertPool()
	if err != nil {
		return nil, err
	}
	pool.AppendCertsFromPEM(caCrt)
	
	var cliCrt tls.Certificate  // 具体的证书加载对象
	cliCrt, err = tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return nil, err
	}
 
	// 把上面的准备内容传入 client
	client = &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				RootCAs:      pool,
				Certificates: []tls.Certificate{cliCrt},
			},
		},
	}
 
	// Get 请求
	resp, err = client.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
 
	body, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	defer client.CloseIdleConnections()
	return body, nil
}

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

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

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

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

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

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

func GetHttpsSkip(url, token string) ([]byte, error) {
 
	// 创建各类对象
	var client *http.Client
	var request *http.Request
	var resp *http.Response
	var body []byte
	var err error
 
	`这里请注意,使用 InsecureSkipVerify: true 来跳过证书验证`
	client = &http.Client{Transport: &http.Transport{
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: true,
		},
	}}
 
	// 获取 request请求
	request, err = http.NewRequest("GET", url, nil)
 
	if err != nil {
		log.Println("GetHttpSkip Request Error:", err)
		return nil, nil
	}
 
	// 加入 token
	request.Header.Add("Authorization", token)
	resp, err = client.Do(request)
	if err != nil {
		log.Println("GetHttpSkip Response Error:", err)
		return nil, nil
	}
	defer resp.Body.Close()
	body, err = ioutil.ReadAll(resp.Body)
	defer client.CloseIdleConnections()
	return body, nil
}

传入 token,验证

func TestGetHttps(t *testing.T) {
	resp, err := GetHttpsSkip("https://10.10.102.91:10250/metrics",
		"Bearer eyxxxxxxxxxxxxxxxxxxxx....xxxxx")
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(string(resp))
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值