前言:大家好,以下所有内容都是我学习韩茹老师的教程时所整理的笔记。部分内容有过删改, 推荐大家去看原作者的文档进行学习, 本文章仅作为个人的学习笔记,后续还会在此基础上不断修改。学习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则对服务器进行支持。
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 “”
错误:不支持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: []
浏览器直接打开网页效果如下:
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.Post | post请求提交指定类型的数据 |
http.PostForm | post请求提交 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方法,比较简单,这里就不再说明。