golang的net/http包已经提供了强大了网络操作函数,我们编写的web客户端示例如下:
package main
import (
"io/ioutil"
"log"
"net/http"
)
func main() {
//Get请求
res, err := http.Get("http://www.baidu.com")
if err != nil {
log.Fatal(err)
}
//利用ioutil包读取百度服务器返回的数据
data, err := ioutil.ReadAll(res.Body)
res.Body.Close()//一定要记得关闭连接
if err != nil {
log.Fatal(err)
}
log.Printf("%s", data)
}
可以通过请求头来控制请求的参数。
设置请求头参数之前需求构造一个请求,从而进行参数设定。我们首先将main函数改造成如下所示:
func main() {
client := &http.Client{}
url := "http://www.baidu.com"
req, err := http.NewRequest("GET", url, nil)//GET大写
if err != nil {
log.Fatal(err)
}
rep, err := client.Do(req)//发起请求
if err != nil {
log.Fatal(err)
}
data, err := ioutil.ReadAll(rep.Body)
rep.Body.Close()
if err != nil {
log.Fatal(err)
}
log.Printf("%s", data)
}
客户端在发送请求时,可以通过设定请求头中的多项参数规范请求的特性,例如字符编码集、语言等等,详细信息大家可以参照:这里
常见的几项设置:
Accept::浏览器可接受的数据类型;
Accept-Charset:浏览器可接受的字符编码集;
Accept-Encoding:浏览器可接受的编码方法;
Accept-Language:浏览器可接受的语言;
Connection:是否为持久连接,默认长连接keep-alive,由于网页数据很多,所以浏览器可能是连续多次请求服务器数据的,此时多次访问只有首次完成tcp3次握手,减少消耗;而tcp中的keep-alive是tcp连接的有效时长,一般比http的keep-alive的时间长。
Cookie:Cookie值;
Content-Lenght:请求内容的长度,过长和果断将导致请求内容截断;如果不知道可以不设置,交由底层完成;
Host:访问的服务器地址;
User-Agent:用户信息。
了解基本几个参数的设置后,将其设置为相应的值即可,执行后即可拿到百度服务器返回的数据。
func main() {
client := &http.Client{}
url := "http://www.baidu.com"
req, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Fatal(err)
}
req.Header.Add("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
req.Header.Add("Accept-Charset","utf-8")
//req.Header.Add("Accept-Encoding","br, gzip, deflate")
req.Header.Add("Accept-Language","zh-cn")
req.Header.Add("Connection","keep-alive")
//req.Header.Add("Cookie","xxxxxxxxxxxxxxx")
//req.Header.Add("Content-Lenght",xxx)
req.Header.Add("Host","www.baidu.com")
req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36")
rep, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
data, err := ioutil.ReadAll(rep.Body)
rep.Body.Close()
if err != nil {
log.Fatal(err)
}
log.Printf("%s", data)
}
百度作为一个搜索平台,我们可以在请求中加入搜索词,然后获取搜索结果。还有很多场景都需要我们提交数据,如注册登陆、Google翻译等等。那如何提交数据呢?这就要用post方法向服务器提交。
func main() {
client := &http.Client{}
req_data := `{"name":"ali", "age":"18"}`
url := "http://www.baidu.com"
req, err := http.NewRequest("POST", url, strings.NewReader(req_data))
if err != nil {
log.Fatal(err)
}
//Content-Type很重要,下文解释
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
//req.Header.Set("Content-Type", "application/json")
//req.Header.Set("Content-Type", "multipart/form-data")
rep, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
data, err := ioutil.ReadAll(rep.Body)
rep.Body.Close()
if err != nil {
log.Fatal(err)
}
log.Printf("%s", data)
}
上面的代码意在向百度提交name和age数据,由于百度服务器并不识别name和age,所以无法返回正确的数据,但流程上是正确的。其中有必要说一下Content-Type,它是用于规定提交数据的类型,常见的有application/x-www-form-urlencoded、application/form-data和application/json等,下面分别说明一下:
application/x-www-form-urlencoded:最常见的类型之一,表示将数据转化为键值对后提交,例如name=ali&age=18.但是如果有本身数据中有&字符就可能造成歧义,例如二进制文件等。
application/form-data:表示以表格数据上报,即将多组数据以一串复杂的boundary数据分隔,而非&符;因此这种数据类型可以用于上传文件或有&符的数据。
application/json:表示将提交的数据转化成json格式。
更多有关content-type的资料请参照这里
结合之前文章中提到的gorutine,sync.WaitGroup等知识,将代码简化封装成如下所示。可以通过修改url的值访问本地服务器。
package main
import (
"io/ioutil"
"log"
"net/http"
"sync"
)
var wc sync.WaitGroup
func HelloClient(client *http.Client, url string){
defer wc.Done()
req, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Fatal(err)
}
rep, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
data, err := ioutil.ReadAll(rep.Body)
rep.Body.Close()
if err != nil {
log.Fatal(err)
}
log.Printf("%s", data)
}
func main() {
client := &http.Client{}
url := "http://127.0.0.1:8000"
wc.Add(10)
for i := 0; i < 10; i++{
go HelloClient(client, url)
}
wc.Wait()
}