一、HTTP知识点补充
- 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
请求一般不应产生副作用。就是说,它仅仅是获取资源信息,就像数据库查询一样,不会修改和增加数据,不会影响资源的状态。注意:这里安全的含义仅仅是指是非修改信息。
(2). 幂等是指对同一个URL的多个请求应该返回同样的结果。
-
POST方法向服务器提交数据,比如完成表单数据的提交,将数据提交给服务器处理。
-
PUT方法是让服务器用请求的主体部分来创建一个由所请求的URL命名的新文档;如果那个文档存在的话,就用这个主体来代替它。
-
DELETE方法就是请求服务器删除指定URL所对应的资源。但是,客户端无法保证删除操作一定会被执行,因为HTTP规范允许服务器在不通知客户端的情况下撤销请求。
-
HEAD方法与GET方法的行为很类似,但服务器在响应中只返回实体的主体部分。这就允许客户端在未获取实际资源的情况下,对资源的首部进行检查,
使用HEAD,我们可以更高效的完成以下工作:
①. 在不获取资源的情况下,了解资源的一些信息,比如资源类型;
②. 通过查看响应中的状态码,可以确定资源是否存在;
③.通过查看首部,测试资源是否被修改。
-
TRACE方法会在目的服务器端发起一个“回环”诊断,我们都知道,客户端在发起一个请求时,这个请求可能要穿过防火墙、代理、网关、或者其它的一些应用程序。这中间的每个节点都可能会修改原始的HTTP请求,TRACE方法允许客户端在最终将请求发送服务器时,它变成了什么样子。由于有一个“回环”诊断,在请求最终到达服务器时,服务器会弹回一条TRACE响应,并在响应主体中携带它收到的原始请求报文的最终模样。这样客户端就可以查看HTTP请求报文在发送的途中,是否被修改过了。
-
OPTIONS方法用于获取当前URL所支持的方法。若请求成功,则它会在HTTP头中包含一个名为“Allow”的头,值是所支持的方法,如“GET, POST”。
二、GET请求
- 基本的get请求
//基本的GET请求
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
resp, err := http.Get("/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")
}
}
- 带参数的get请求
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main(){
resp, err := http.Get("/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("/get")
if err != nil {
return
}
params.Set("name","zhangsan")
params.Set("age","23")
//如果参数中有中文参数,这个方法会进行URLEncode
Url.RawQuery = params.Encode()
urlPath := Url.String()
fmt.Println(urlPath) // /get?age=23&name=zhangsan
resp,err := http.Get(urlPath)
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}
- 解析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("/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)
}
请求添加请求头
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
client := &http.Client{}
req,_ := http.NewRequest("GET","/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请求
- 基本的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("/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": "",
"User-Agent": "Go-http-client/1.1"
},
"json": null,
"origin": "211.138.20.170, 211.138.20.170",
"url": "/post"
}
- 另一种方式
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("/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": "",
"User-Agent": "Go-http-client/1.1"
},
"json": null,
"origin": "211.138.20.170, 211.138.20.170",
"url": "/post"
}
- 发送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","/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": "",
"User-Agent": "Go-http-client/1.1"
},
"json": {
"age": "23",
"name": "zhangsan"
},
"origin": "211.138.20.170, 211.138.20.170",
"url": "/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(":xxxx/metrics",
"C:/Users/Desktop/",
"C:/Users/Desktop/",
"C:/UsersDesktop/")
if err != nil {
fmt.Println(err)
}
fmt.Println(string(resp))
}
这里使用了 “crypto” 标准库,其中,我们使用
- tls.LoadX509KeyPair()方法读取证书路径,转换为证书对象;
- ()方法创建证书池;
- (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))
}