目录
写在前面
上一篇我们介绍了使用 net/http
发送get请求,因为考虑到篇幅问题,把Post单独拎了出来,我们在这一篇一起从源码来了解一下Golang的Post请求。
发送Post请求
net/http
发送Post请求很容易,下面的代码我们和Get请求一样,把响应的内容的信息打印出来了,细心的朋友可能会发现,在参数传递、和结果解析时用了三种不同的方式,我们将在后面进行解析。
示例代码
python
package main
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"reflect"
"strings"
)
func main() {
resp, err := http.Post("http://httpbin.org/post",
"application/x-www-form-urlencoded",
strings.NewReader("name=Detector&mobile=1xxxxxxxx"))
if err != nil {
fmt.Println(err)
return
}
defer resp.Body.Close()
headers := resp.Header
// headers 打印报文头部信息
for k, v := range headers {
fmt.Printf("%v, %v\n", k, v) // %v 打印interfac{}的值
}
// 打印响应信息内容
fmt.Printf("响应状态:%s,响应码: %d\n", resp.Status, resp.StatusCode)
fmt.Printf("协议:%s\n", resp.Proto)
fmt.Printf("响应内容长度: %d\n", resp.ContentLength)
fmt.Printf("编码格式:%v\n", resp.TransferEncoding) // 未指定时为空
fmt.Printf("是否压缩:%t\n", resp.Uncompressed)
fmt.Println(reflect.TypeOf(resp.Body)) // *http.gzipReader
fmt.Println(resp.Body)
buf := bytes.NewBuffer(make([]byte, 0, 512))
length, _ := buf.ReadFrom(resp.Body)
fmt.Println(len(buf.Bytes()))
fmt.Println(length)
fmt.Println(string(buf.Bytes()))
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
}
源码分析
Post请求参数解析
我们首先来看一下C:\Go\src\net\http\client.go
中Post和Get请求的源码:
python
func Get(url string) (resp *Response, err error) {
return DefaultClient.Get(url)
}
python
func Post(url string, contentType string, body io.Reader) (resp *Response, err error) {
return DefaultClient.Post(url, contentType, body)
}
从上面的定义可以看出,Post请求的参数比Get复杂一些,不仅要传递string类型contentType
还有传递io.Reader
类型的body体。可能有的小伙伴就有疑问了-- io.Reader
类型的body体是不是意味着我们一定要使用io.Reader
模块来获取数据呢?
答案当然是否定的。
我们通过阅读源码,来找想要的答案。
找到其最小粒度的接口是一个比较好的手段,io.Reader
最小粒度的接口的定义在C:\Go\src\io\io.go
中:
python
type Reader interface {
Read(p []byte) (n int, err error)
}
在我前面的一篇博客【Golang】基础10 Go语言最精妙的设计--interface中学习过 interface,其中有两句话是这样的:
interface类型定义了一组方法,如果某个对象实现了某个接口的所有方法,则此对象就实现了此接口。如果我们定义了一个interface的变量,那么这个变量里面可以存实现这个interface的任意类型的对象。
针对io.Reader
的定义翻译一下就是,只要实现了Read(p []byte) (n int, err error)
方法的类型,就可以存储io.Reader
,从而作为Post请求的Body参数。
我们接着来看看响应resp中数据的定义。
响应数据解析
在上一篇我们对http请求中的数据进行了介绍,这一次我们针对resp.Body
进行展开。
在C:\Go\src\net\http\response.go
中我们可以看到它的类型Body io.ReadCloser
,在C:\Go\src\io\io.go
中我们可以看到对应的定义是这样的:
python
type ReadCloser interface {
Reader
Closer
}
而Reader就是我们上面分析的过的请求body定义的Reader,而Closer是一个error
类型:
python
type Closer interface {
Close() error
}
根据我们上面的结论-- 定义的某个接口的变量可以存储同样实现该接口的任意类型对象 ,即是说任意类型,只要实现了 Reader 和 Closer 即可以用来解析resp.Body
。
那我们来验证一下示例里面使用的三种方法是不是符合我们这个结论。
验证
-
strings.NewReader
我们可以在
C:\Go\src\strings\reader.go
看到 Reader 类型的Read
方法: -
bytes.NewBuffer
我们可以在
C:\Go\src\bytes\buffer.go
看到 Buffer 类型的Read
方法: -
ioutil.ReadAll
我们可以在
C:\Go\src\io\ioutil\ioutil.go
看到 io.Reader 类型的ReadAll
和Close() error
方法:
发送Json/XMl
在了解了上面的知识之后,我们再来看发送Json、XML数据等就比较简单了。
Json请求示例代码
python
func JsonReq() {
info := make(map[string]interface{})
info["name"] = "Detector"
info["age"] = 15
info["loc"] = "深圳"
// 将map解析未[]byte类型
bytesData, _ := json.Marshal(info)
// 将解析之后的数据转为*Reader类型
reader := bytes.NewReader(bytesData)
println(reader)
resp, _ := http.Post("http://httpbin.org/post",
"application/json",
reader)
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}
代码里面给了一些注释,我们把要发送的数据转化为了 *Reader
类型,然后就可以直接发送了。(我们从上面的源码分析可以知道这个类型是可以存储io.Reader
的数据的)
xml请求示例代码
python
func XMLReq() {
xml := `<?xml version="1.0" encoding="UTF-8"?>
<resources>
<string name="VideoLoading">Loading video...</string>
<string name="ApplicationName">what</string>
</resources>`
bytesData := strings.NewReader(xml)
resp, _ := http.Post("http://httpbin.org/post",
"application/xml",
bytesData)
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}
总结
- Json、xml请求
- 请求、响应数据简析
- interface概念复习
资料获取方法
【留言777】
各位想获取源码等教程资料的朋友请 点赞 + 评论 + 收藏 ,三连!
三连之后我会在评论区挨个私信发给你们~