Go教程

【Go Web学习笔记】第二章 HTTP实现客户端

本文主要是介绍【Go Web学习笔记】第二章 HTTP实现客户端,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

前言:大家好,以下所有内容都是我学习韩茹老师的教程时所整理的笔记。部分内容有过删改, 推荐大家去看原作者的文档进行学习本文章仅作为个人的学习笔记,后续还会在此基础上不断修改。学习Go Web时应该已经熟悉Go语言基本语法以及计算机网络的相关内容。

学习链接:https://www.chaindesk.cn/witbook/17/253
参考书籍:《Go Web编程实战派》

第二章、HTTP实现客户端

在上节课我们通过搭建一个简单的Go Web服务器,已经了解过http包。下面就介绍下如何通过http协议去请求。

go语言中提供了net/http库去进行网络请求,我们在import中需要导入我们用到的库。

net/http,和html/template这两个标准库,是实现Go Web最核心的。net/http标准库可以分为客户端和服务器两个部分,库中的结构和函数,有些只支持客户端和服务器这两者中的一个,而有些则同时支持客户端和服务器:

  • Client,Response,Header,Request和Cookie对客户端进行支持。
  • Server,ServeMux,Handler/HandleFunc,ResponseWriter,Header,Request和Cookie则对服务器进行支持。

http://img.kongyixueyuan.com/net_http.png

1、发送请求

1.1 scheme

下面来看一个简单的get实现,创建项目,新建一个go文件(demo01_client.go):

package main

import (
    "net/http"
    "fmt"
)

func main() {
    requestUrl := "www.baidu.com"
    //requestUrl := "http://www.baidu.com"
    response, err := http.Get(requestUrl)
    if err != nil {
        fmt.Println("err:", err)
    }
    defer response.Body.Close()
    fmt.Println(response.Body)
}

然后就报错了:err: Get www.baidu.com: unsupported protocol scheme “”

image-20211120210123976

错误:不支持scheme为空的协议,这是什么情况呢,

requestUrl改为requestUrl := "http://www.baidu.com"

scheme反应出的就是协议名称。

重新运行程序,可以正常执行了,运行结果:

&{[] 0xc0000a6080 <nil> <nil>}

1.2 Response

response中的参数一般也比较多,我们需要的最多的通常是Body参数,Body是 io.ReadCloser 这个类型的对象。用 ioutil.ReadAll 去转化 io.ReadCloser 类型,输出是 byty[](补充:这里应该是 byte[]),再通过string()强制转换就能看到string了。

if response.StatusCode == 200 {
    r, err := ioutil.ReadAll(response.Body)
    if err != nil {
        fmt.Println(err)
    }
    return string(r)
}

正确获取到了服务器响应的数据才能这么打印出来了,假如失败了呢,因此需要判断返回码,再去获取body的数据。

最后要记得关闭。

demo02_response.go的详细代码:

package main

import (
    "net/http"
    "log"
    "fmt"
    "io/ioutil"
)

func main() {
    urlStr := "http://www.webxml.com.cn/WebServices/WeatherWebService.asmx/getWeatherbyCityName?theCityName=北京"
    response, err := http.Get(urlStr)
    if err != nil {
        log.Fatal(err)
    }
    defer response.Body.Close()

    if response.StatusCode == 200 {
        body, err := ioutil.ReadAll(response.Body)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println(string(body))
    }

    fmt.Println("-----------------------------------------")
    fmt.Printf("response:%+v\n", response)
    fmt.Println("-----------------------------------------")
    fmt.Printf("response.Body:%+v\n", response.Body)
    fmt.Printf("response.Header:%+v\n", response.Header)
    fmt.Printf("response.StatusCode:%+v\n", response.StatusCode)
    fmt.Printf("response.Status:%+v\n", response.Status)
    fmt.Printf("response.Request:%+v\n", response.Request)
    fmt.Printf("response.Cookies:%+v\n", response.Cookies())
}

运行结果:

<?xml version="1.0" encoding="utf-8"?>
<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://WebXml.com.cn/">
  <string>直辖市</string>
  <string>北京</string>
  <string>54511</string>
  <string>54511.jpg</string>
  <string>2021/11/20 21:19:15</string>
  <string>0℃/10℃</string>
  <string>11月20日 多云</string>
  <string>北风转南风小于3级</string>
  <string>1.gif</string>
  <string>1.gif</string>
  <string>今日天气实况:气温:5℃;风向/风力:南风 1级;湿度:65%;紫外线强度:弱。</string>
  <string>感冒指数:少发,无明显降温,感冒机率较低。
运动指数:较适宜,气温较低,在户外运动请注意增减衣物。
过敏指数:极不易发,无需担心过敏,可放心外出,享受生活。
穿衣指数:较冷,建议着厚外套加毛衣等服装。
洗车指数:适宜,天气较好,适合擦洗汽车。
紫外线指数:弱,辐射较弱,涂擦SPF12-15、PA+护肤品。
</string>
  <string>-2℃/8℃</string>
  <string>11月21日 多云</string>
  <string>西北风4-5级</string>
  <string>1.gif</string>
  <string>1.gif</string>
  <string>-5℃/4℃</string>
  <string>11月22日 晴</string>
  <string>西北风4-5级转西风小于3级</string>
  <string>0.gif</string>
  <string>0.gif</string>
  <string>北京位于华北平原西北边缘,市中心位于北纬39度,东经116度,四周被河北省围着,东南和天津市相接。全市面积一万六千多平方公里,辖12区6县,人口1100余万。北京为暖温带半湿润大陆性季风气候,夏季炎热多雨,冬季寒冷干燥,春、秋短促,年平均气温10-12摄氏度。北京是世界历史文化名城和古都之一。早在七十万年前,北京周口店地区就出现了原始人群部落“北京人”。而北京建城也已有两千多年的历史,最初见于记载的名字为“蓟”。公元前1045年北京成为蓟、燕等诸侯国的都城;公元前221年秦始皇统一中国以来,北京一直是中国北方重镇和地方中心;自公元938年以来,北京又先后成为辽陪都、金上都、元大都、明清国都。1949年10月1日正式定为中华人民共和国首都。北京具有丰富的旅游资源,对外开放的旅游景点达200多处,有世界上最大的皇宫紫禁城、祭天神庙天坛、皇家花园北海、皇家园林颐和园,还有八达岭、慕田峪、司马台长城以及世界上最大的四合院恭王府等各胜古迹。全市共有文物古迹7309项,其中国家文物保护单位42个,市级文物保护单位222个。北京的市树为国槐和侧柏,市花为月季和菊花。另外,北京出产的象牙雕刻、玉器雕刻、景泰蓝、地毯等传统手工艺品驰誉世界。</string>
</ArrayOfString>
-------------------------
response: &{Status:200 OK StatusCode:200 Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 Header:map[Cache-Control:[private, max-age=0] Content-Type:[text/xml; charset=utf-8] Date:[Sat, 20 Nov 2021 13:22:37 GMT] Server:[Microsoft-IIS/7.5] Vary:[Accept-Encoding] X-Aspnet-Version:[2.0.50727] X-Powered-By:[ASP.NET]] Body:0xc000048060 ContentLength:-1 TransferEncoding:[] Close:false Uncompressed:true Trailer:map[] Request:0xc000114000 TLS:<nil>}
-------------------------
response.Body: &{_:[] body:0xc000026080 zr:0xc00004c2c0 zerr:<nil>}
response.Header: map[Cache-Control:[private, max-age=0] Content-Type:[text/xml; charset=utf-8] Date:[Sat, 20 Nov 2021 13:22:37 GMT] Server:[Microsoft-IIS/7.5] Vary:[Accept-Encoding] X-Aspnet-Version:[2.0.50727] X-Powered-By:[ASP.NET]]
response.StatusCode: 200
response.Status: 200 OK
response.Request: &{Method:GET URL:http://www.webxml.com.cn/WebServices/WeatherWebService.asmx/getWeatherbyCityName?theCityName=北京 Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 Header:map[] Body:<nil> GetBody:<nil> ContentLength:0 TransferEncoding:[] Close:false Host:www.webxml.com.cn Form:map[] PostForm:map[] MultipartForm:<nil> Trailer:map[] RemoteAddr: RequestURI: TLS:<nil> Cancel:<nil> Response:<nil> ctx:0xc0000ba058}
response.Cookies: []

浏览器直接打开网页效果如下:

image-20211120212433632

2、客户端的实现

go语言的客户端有几种实现方式。

2.1 方式一 使用http.NewRequest

先生成 http.client -> 再生成 http.request -> 之后提交请求:client.Do(request) -> 处理返回结果,每一步的过程都可以设置一些具体的参数。

新建go文件(demo03_NewRequest.go),示例代码如下:

package main

import (
    "net/http"
    "log"
    "fmt"
    "io/ioutil"
)

func main() {
    /**
    客户端实现方式一:
    1.先生成 http.client ->
    2.再生成 http.request ->
    3.之后提交请求:client.Do(request) ->
    4.处理返回结果,每一步的过程都可以设置一些具体的参数。
     */
    urlStr := "http://www.webxml.com.cn/WebServices/WeatherWebService.asmx/getWeatherbyCityName?theCityName=北京"
    //step1:创建client对象
    client := http.Client{}
    //step2:创建request
    request, err := http.NewRequest("GET", urlStr, nil)
    if err != nil {
        log.Fatal(err)
    }
    //step3:发送请求
    response, err := client.Do(request)
    if err != nil {
        log.Fatal(err)
    }
    defer response.Body.Close()

    if response.StatusCode == 200 {
        body, err := ioutil.ReadAll(response.Body)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println(string(body))
    }

    fmt.Println("-----------------------------------------")
    fmt.Printf("response:%+v\n", response)
    fmt.Println("-----------------------------------------")
    fmt.Printf("response.Body:%+v\n", response.Body)
    fmt.Printf("response.Header:%+v\n", response.Header)
    fmt.Printf("response.StatusCode:%+v\n", response.StatusCode)
    fmt.Printf("response.Status:%+v\n", response.Status)
    fmt.Printf("response.Request:%+v\n", response.Request)
    fmt.Printf("response.Cookies:%+v\n", response.Cookies())
}

也可以使用默认的DefaultClient对象。

新建go文件(demo04_defaultclient.go),实例代码如下:

package main

import (
    "net/http"
    "net/http/httputil"
    "fmt"
)

func main() {
    request, err := http.NewRequest(http.MethodGet, "http://www.chaindesk.cn/", nil)
    request.Header.Add("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1")

    resp, err := http.DefaultClient.Do(request)
    //resp, err := http.Get("http://www.chaindesk.cn/")
    //resp, err := http.Get("http://www.baidu.com/")
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    s, err := httputil.DumpResponse(resp, true)
    if err != nil{
        panic(err)
    }
    fmt.Printf("%s\n", s)
}

2.2 方式二 调用client的API

先生成client,之后用 client.get/post…

client 结构自己也有一些发送 api 的方法,比如 client.get, client.post, client.postform… 等等。基本上涵盖了主要的http请求的类型,通常不进行什么特殊的配置的话,这样就可以了,其实client的 get 或者 post 方法,也是对 http.Newerequest 方法的封装,里面还额外添加了 req.Header.Set(“Content-Type”, bodyType) 一般用的话,也是 ok 的。

新建go文件(demo05_client_get.go),示例代码:

package main

import (
    "net/http"
    "log"
    "fmt"
    "io/ioutil"
)

func main() {
    /**
    方式二 调用client的API

    1.先生成client,
    2.之后用client.get/post..

    还额外添加了req.Header.Set("Content-Type", bodyType)...
     */
    urlStr := "http://www.webxml.com.cn/WebServices/WeatherWebService.asmx/getWeatherbyCityName?theCityName=北京"
    //step1:创建client对象
    client := http.Client{}

    //step2:调用get方法,发送请求
    response, err := client.Get(urlStr)
    if err != nil {
        log.Fatal(err)
    }
    defer response.Body.Close()

    if response.StatusCode == 200 {
        body, err := ioutil.ReadAll(response.Body)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println(string(body))
    }
    fmt.Printf("%+v", response)
}

3、客户端请求

可以通过之前的方式,先创建client对象,然后调用 client.Get(url) 方法,或者是 client.Post(url, contentType, body) 实现get或post的请求。也可以通过http直接进行调用。

golang的标准api中用于http客户端请求的主要有三个api : http.Get,http.Post,http.PostForm,其区别如下:

API特点
http.Get发送get请求
http.Postpost请求提交指定类型的数据
http.PostFormpost请求提交 application/x-www-form-urlencoded 数据

在使用http客户端api的时候要注意一个问题:请求地址的url必须是带http://协议头的完整url, 不然请求结果为空。

3.1 Get请求

http包中Get()方法的源代码:

func Get(url string) (resp *Response, err error) {
    return DefaultClient.Get(url)
}

新建go文件(demo06_http_get.go),示例代码:

package main

import (
    "net/http"
    "fmt"
    "io/ioutil"
    "log"
)

func main() {
    resp, err := http.Get("http://www.baidu.com")
    if err != nil {
        // handle error
        log.Fatal(err)
    }

    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        // handle error
        log.Fatal(err)
    }

    fmt.Println(string(body))
}

3.2 Post请求

3.2.1 post()方法

post请求可以通过client对象调用 Post() 方法,也可以通过http直接进行调用。

client对象的 Post() 方法源代码:

func (c *Client) Post(url string, contentType string, body io.Reader) (resp *Response, err error) {
    req, err := NewRequest("POST", url, body)
    if err != nil {
        return nil, err
    }
    req.Header.Set("Content-Type", contentType)
    return c.Do(req)
}

新建go文件(demo07_client_post.go),示例代码:

package main

import (
    "net/http"
    "log"
    "fmt"
    "io/ioutil"
    "bytes"
    "net/url"
)

func main() {
    urlStr := "http://www.webxml.com.cn/WebServices/WeatherWebService.asmx/getWeatherbyCityName"
    client := http.Client{}
    param := &url.Values{
        "theCityName": {"苏州"},
    }
    requestBody := bytes.NewBufferString(param.Encode())
    response, err := client.Post(urlStr, "application/x-www-form-urlencoded", requestBody)
    if err != nil {
        log.Fatal(err)
    }
    defer response.Body.Close()

    if response.StatusCode == 200 {
        body, err := ioutil.ReadAll(response.Body)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println(string(body))
    }
    fmt.Printf("%+v", response)
}

也可以通过http包的Post()直接实现,源代码如下:

func Post(url string, contentType string, body io.Reader) (resp *Response, err error) {
    return DefaultClient.Post(url, contentType, body)
}

新建go文件(demo08_http_post.go),示例代码:

package main

import (
    "net/http"
    "strings"
    "io/ioutil"
    "fmt"
)

func main() {
    resp, err := http.Post("http://www.webxml.com.cn/WebServices/WeatherWebService.asmx/getWeatherbyCityName",
        "application/x-www-form-urlencoded",
        strings.NewReader("theCityName=苏州"))
    if err != nil {
        fmt.Println(err)
    }

    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        // handle error
    }

    fmt.Println(string(body))
}

注意:使用post方法,要设置contentType,设置成 “application/x-www-form-urlencoded”,否则post参数无法传递。

也可以自己搭建服务器:

新建go文件(demo09_post.go),客户端代码:

package main

import (
    "net/http"
    "net/url"
    "io/ioutil"
    "fmt"
    "bytes"
    "log"
)

func main() {
    /**
    也可以使用自己搭建的服务器
     */
    urlStr := "http://localhost:2001/login"
    param := url.Values{
        "username": {"嘘嘘嘘大学"},
        "password": {"ClimberCoding12121"},
    }
    requestBody := bytes.NewBufferString(param.Encode())
    response, err := http.Post(urlStr, "application/x-www-form-urlencoded", requestBody)

    if err != nil {
        log.Fatal(err)
    }
    defer response.Body.Close()

    if response.StatusCode == 200 {
        body, err := ioutil.ReadAll(response.Body)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println(string(body))
    }
    fmt.Printf("response: %+v\n", response)
    fmt.Printf("response.Header: %+v\n", response.Header)
    fmt.Printf("response.Cookies: %+v\n", response.Cookies())
}

新建go文件(demo09_server.go),服务器端代码:

package main

import (
    "net/http"
    "io/ioutil"
    "log"
    "fmt"
    "io"
)

func main() {
    server := http.NewServeMux()		//路由
    server.HandleFunc("/login", login)
    http.ListenAndServe(":2001", server)
}

func login(w http.ResponseWriter, r *http.Request) {
    r.ParseForm()
    body, err := ioutil.ReadAll(r.Body)
    r.Body.Close()

    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("body", string(body))
    fmt.Printf("r: %+v\n", r)
    fmt.Printf("%+v\n", r.Header)
    fmt.Printf("%+v\n", r.Header["Content-Type"])
    fmt.Printf("%+v\n", r.Cookies())

    if len(r.Form["username"]) > 0 {
        username := r.Form["username"][0]
        fmt.Println("username: ", username)
    }
    if len(r.Form["password"]) > 0 {
        password := r.Form["password"][0]
        fmt.Println("password: ", password)
    }

    io.WriteString(w, "登录成功")
}

先启动服务端,再启动客户端,

服务端运行结果:

body 
r: &{Method:POST URL:/login Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 Header:map[Accept-Encoding:[gzip] Content-Length:[82] Content-Type:[application/x-www-form-urlencoded] User-Agent:[Go-http-client/1.1]] Body:0xc000026140 GetBody:<nil> ContentLength:82 TransferEncoding:[] Close:false Host:localhost:2001 Form:map[password:[ClimberCoding12121] username:[嘘嘘嘘大学]] PostForm:map[password:[ClimberCoding12121] username:[嘘嘘嘘大学]] MultipartForm:<nil> Trailer:map[] RemoteAddr:[::1]:54243 RequestURI:/login TLS:<nil> Cancel:<nil> Response:<nil> ctx:0xc000026180}
map[Accept-Encoding:[gzip] Content-Length:[82] Content-Type:[application/x-www-form-urlencoded] User-Agent:[Go-http-client/1.1]]
[application/x-www-form-urlencoded]
[]
username:  嘘嘘嘘大学
password:  ClimberCoding12121

客户端运行结果:

登录成功
response: &{Status:200 OK StatusCode:200 Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 Header:map[Content-Length:[12] Content-Type:[text/plain; charset=utf-8] Date:[Sat, 20 Nov 2021 14:44:24 GMT]] Body:0xc000198040 ContentLength:12 TransferEncoding:[] Close:false Uncompressed:false Trailer:map[] Request:0xc000136000 TLS:<nil>}
response.Header: map[Content-Length:[12] Content-Type:[text/plain; charset=utf-8] Date:[Sat, 20 Nov 2021 14:44:24 GMT]]
response.Cookies: []
3.2.2 postForm()方法

http包下的PostForm()方法源代码:

func PostForm(url string, data url.Values) (resp *Response, err error) {
    return DefaultClient.PostForm(url, data)
}

新建go文件(demo10_postform.go),客户端实例代码:

package main

import (
    "net/http"
    "net/url"
    "io/ioutil"
    "fmt"
    "log"
)

func main() {
    /**
    还是使用上一个服务器
    使用http.PostForm()
     */
    response, err := http.PostForm("http://localhost:2001/login",
        url.Values{"username": {"嘘嘘嘘大学"}, "password": {"ClimberCoding12345"}})

    if err != nil {
        log.Fatal(err)
    }
    defer response.Body.Close()

    if response.StatusCode == 200 {
        body, err := ioutil.ReadAll(response.Body)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println(string(body))
    }
    fmt.Printf("response: %+v\n", response)
    fmt.Printf("response.Header: %+v\n", response.Header)
    fmt.Printf("response.Cookies: %+v\n", response.Cookies())
}

服务器端继续使用上次的代码,不用改动。

服务器端运行结果:

body 
r: &{Method:POST URL:/login Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 Header:map[Accept-Encoding:[gzip] Content-Length:[82] Content-Type:[application/x-www-form-urlencoded] User-Agent:[Go-http-client/1.1]] Body:0xc00008a0c0 GetBody:<nil> ContentLength:82 TransferEncoding:[] Close:false Host:localhost:2001 Form:map[password:[ClimberCoding12345] username:[嘘嘘嘘大学]] PostForm:map[password:[ClimberCoding12345] username:[嘘嘘嘘大学]] MultipartForm:<nil> Trailer:map[] RemoteAddr:[::1]:54327 RequestURI:/login TLS:<nil> Cancel:<nil> Response:<nil> ctx:0xc00008a100}
map[Accept-Encoding:[gzip] Content-Length:[82] Content-Type:[application/x-www-form-urlencoded] User-Agent:[Go-http-client/1.1]]
[application/x-www-form-urlencoded]
[]
username:  嘘嘘嘘大学
password:  ClimberCoding12345

客户端运行结果:

登录成功
response: &{Status:200 OK StatusCode:200 Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 Header:map[Content-Length:[12] Content-Type:[text/plain; charset=utf-8] Date:[Sat, 20 Nov 2021 14:49:53 GMT]] Body:0xc00020c040 ContentLength:12 TransferEncoding:[] Close:false Uncompressed:false Trailer:map[] Request:0xc000136000 TLS:<nil>}
response.Header: map[Content-Length:[12] Content-Type:[text/plain; charset=utf-8] Date:[Sat, 20 Nov 2021 14:49:53 GMT]]
response.Cookies: []
3.2.3 复杂的请求

有时需要在请求的时候设置头参数、cookie之类的数据,需要使用http.Do方法。

设置请求头:

request.Header.Set("Content-Type", "application/x-www-form-urlencoded")

Set()方法源代码:

func (h Header) Set(key, value string) {
    textproto.MIMEHeader(h).Set(key, value)
}

设置Cookie:

方式一:通过设置请求头

request.Header.Set("Cookie", "name=hanru")

方式二:通过request对象,直接调用AddCookie()方法。

func (r *Request) AddCookie(c *Cookie) {
    s := fmt.Sprintf("%s=%s", sanitizeCookieName(c.Name), sanitizeCookieValue(c.Value))
    if c := r.Header.Get("Cookie"); c != "" {
        r.Header.Set("Cookie", c+"; "+s)
    } else {
        r.Header.Set("Cookie", s)
    }
}

新建go文件(demo11_client.go),客户端实例代码:

package main

import (
    "bytes"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "net/url"
)

func main() {
    urlStr := "http://localhost:2000/login"
    client := http.Client{}
    param := url.Values{
        "username": {"ClimberCoding"},
    }
    //jsonStr, _ := json.Marshal(param)
    //requestBody := bytes.NewBufferString(string(jsonStr))

    requestBody := bytes.NewBufferString(param.Encode())
    request, err := http.NewRequest("POST", urlStr, requestBody)
    if err != nil {
        log.Fatal(err)
    }

    /*
    cookie的添加
    方式一:
        request.Header.Set("Cookie", "name=ClimberCoding")

    方式二:
        request.AddCookie(Cookie)

     */
    cookId := &http.Cookie{Name: "userId", Value: "1234"}
    cookName := &http.Cookie{Name: "name", Value: "xuxuxudaxue"}
    request.AddCookie(cookId)
    request.AddCookie(cookName)
    //使用text/html就会出错
    //application/x-www-form-urlencoded
    request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
    response, err := client.Do(request)
    if err != nil {
        log.Fatal(err)
    }
    defer response.Body.Close()

    if response.StatusCode == 200 {
        body, err := ioutil.ReadAll(response.Body)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println(string(body))
    }
    fmt.Printf("response: %+v\n", response)
    fmt.Printf("response.Header: %+v\n", response.Header)
    fmt.Printf("response.Cookies: %+v\n", response.Cookies())
    fmt.Printf("request.Header: %+v\n", request.Header)
    fmt.Printf("request.Cookies: %+v\n", request.Cookies())
}

新建go文件(demo12_server.go),服务器端代码:

package main

import (
    "log"
    "net/http"
    "io"
    "io/ioutil"
    "fmt"
)

func HandleRequest(w http.ResponseWriter, r *http.Request) {
    r.ParseForm()
    body, err := ioutil.ReadAll(r.Body)
    r.Body.Close()

    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("body", string(body))
    fmt.Printf("r: %+v\n", r)
    fmt.Printf("Request:Header:%+v\n", r.Header)
    fmt.Printf("cookies: %+v\n", r.Cookies())
    cookid, err := r.Cookie("userId")
    if err == nil {
        fmt.Println(cookid.Name, cookid.Value)
    }

    if len(r.Form["username"]) > 0 {
        username := r.Form["username"][0]
        fmt.Println("username:", username)
    }

    w.Header().Set("Access-Control-Allow-Origin", "*")
    w.Header().Set("Access-Control-Allow-Headers", "*")
    io.WriteString(w, "login success ....")
}

func main() {
    http.HandleFunc("/login", HandleRequest)
    err := http.ListenAndServe(":2000", nil)
    if err != nil {
        log.Fatal(err)
    }
}

服务器运行结果:

body 
r: &{Method:POST URL:/login Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 Header:map[Accept-Encoding:[gzip] Content-Length:[22] Content-Type:[application/x-www-form-urlencoded] Cookie:[userId=1234; name=xuxuxudaxue] User-Agent:[Go-http-client/1.1]] Body:0xc00008a0c0 GetBody:<nil> ContentLength:22 TransferEncoding:[] Close:false Host:localhost:2000 Form:map[username:[ClimberCoding]] PostForm:map[username:[ClimberCoding]] MultipartForm:<nil> Trailer:map[] RemoteAddr:[::1]:54385 RequestURI:/login TLS:<nil> Cancel:<nil> Response:<nil> ctx:0xc00008a100}
Request:Header:map[Accept-Encoding:[gzip] Content-Length:[22] Content-Type:[application/x-www-form-urlencoded] Cookie:[userId=1234; name=xuxuxudaxue] User-Agent:[Go-http-client/1.1]]
cookies: [userId=1234 name=xuxuxudaxue]
userId 1234
username: ClimberCoding

客户端运行结果:

login success ....
response: &{Status:200 OK StatusCode:200 Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 Header:map[Access-Control-Allow-Headers:[*] Access-Control-Allow-Origin:[*] Content-Length:[18] Content-Type:[text/plain; charset=utf-8] Date:[Sat, 20 Nov 2021 14:54:37 GMT]] Body:0xc00020e040 ContentLength:18 TransferEncoding:[] Close:false Uncompressed:false Trailer:map[] Request:0xc000136000 TLS:<nil>}
response.Header: map[Access-Control-Allow-Headers:[*] Access-Control-Allow-Origin:[*] Content-Length:[18] Content-Type:[text/plain; charset=utf-8] Date:[Sat, 20 Nov 2021 14:54:37 GMT]]
response.Cookies: []
request.Header: map[Content-Type:[application/x-www-form-urlencoded] Cookie:[userId=1234; name=xuxuxudaxue]]
request.Cookies: [userId=1234 name=xuxuxudaxue]

如果要发起head请求可以直接使用http client的head方法,比较简单,这里就不再说明。

源代码

这篇关于【Go Web学习笔记】第二章 HTTP实现客户端的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!