一、golang http客户端
1. 基础知识:http请求四种常见的POST提交数据方式
**服务端通常是根据请求头(headers)中的 Content-Type 字段来获知请 求中的消息主体是用何种方式编码,再对主体进行解析。**也就是说, Content-Type 指定了消息主体中的编码方式 。因 此,POST 提交数据方案,直接跟 Content-Type 和消息主体两部分有关。
http请求常见的content-type分为4种:application/json、x-www-form-urlencoded、multipart/form-data、text/plain。  1)application/x-www-form-urlencoded
HTTP中默认的提交数据的方式。浏览器的原生表单,如果不设置enctype属性,那么最终就会以application/x-www-form-urlencoded方式提交数据。
const (
http_address = "http://yourIP:8080/****"
)
formValues := url.Values{}
formValues .Set("first",firstData)
formValues .Set("second",secondData)
formDataStr := formValues.Encode()
formDataBytes := []byte(formDataStr )
formBytesReader := bytes.NewReader(formDataBytes )
//生成post请求
client := &http.Client{}
req, err := http.NewRequest("POST", http_address, formBytesReader )
if err != nil {
// handle error
log.Fatal("生成请求失败!",err)
return
}
//注意别忘了设置header
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
//Do方法发送请求
resp, err := client.Do(req)
2)multipart/form-data
一个常见的POST数据提交的方式。我们使用表单上传文件时,必须将enctype设为multipart/form-data。
上面两种 POST 数据方式,都是浏览器原生支持的,而且现阶段原生 form 表单也只支持这两种方式。但
3)application/json
application/json作为请求头,用来告诉服务端消息主体是序列化的JSON字符串。方便提交复杂的结构化数据,特别适合RESTFul接口。 各大抓包工具如Chrome自带的开发者工具、Firebug、Fiddler,都会以树形结构展示JSON数据,非常友好。
Google 的 AngularJS 中的 Ajax 功能,默认就是提交 JSON 字符串。
4)text/xml XML-RPC(XML Remote Procdure Call)。它是一种使用HTTP作为传输协议,XML作为编码方式的远程调用规范。
二、golang 标准库net/http包
golang系列——实战http客户端 参考URL: https://zhuanlan.zhihu.com/p/134146738
golang的net/http包已经提供了强大了网络操作函数。
1. golang使用http client发起get和post请求示例
get请求:
package csdn
import (
"io/ioutil"
"log"
"net/http"
"testing"
)
func TestNewPublisher(t *testing.T) {
res, err := http.Get("http://www.csdn.com")
if err != nil {
log.Fatal(err)
}
data, err := ioutil.ReadAll(res.Body)
res.Body.Close()
if err != nil {
log.Fatal(err)
}
log.Printf("%s", data)
}
2. Golang http添加cookie
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
client := &http.Client{}
req, _ := http.NewRequest("GET", "http://localhost:8082/cookie_test",nil)
req.Header.Add("Cookie","ds=req.Header.Add")
resp, err := client.Do(req)
if err != nil {
panic(nil)
}
body, _ := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
fmt.Println(string(body))
}
工作技巧: 使用浏览器的cookie参数时您可以先拿postman测试验证没有问题,再写到代码中。
3. Go post方式发送带有form-data参数的http请求
golang学习之如何构造一个multipart/form格式的HTTP请求 参考URL: http://blog.codeg.cn/2015/03/18/golang-how-to-make-a-multipart-http-request/ https://www.easck.com/cos/2020/1218/594591.shtml
思路和方法:通过Go标准库提供的mime/multipart包,我们可以很容易地构建出满足要求的包体。
3.1 go标准库的学习-mime/multipart
go标准库的学习-mime/multipart 参考URL: https://www.cnblogs.com/wanghui-garcia/p/10402796.html
import "mime/multipart"
multipart实现了MIME的multipart解析,参见RFC 2046。该实现适用于HTTP(RFC 2388)和常见浏览器生成的multipart主体。
案例一:普通普通表单上传后端程序
func main() {
buf:=new(bytes.Buffer)
bodywriter:=multipart.NewWriter(buf)
bodywriter.WriteField("name","lisi")
bodywriter.WriteField("age","40")
contentType:=bodywriter.FormDataContentType()
defer bodywriter.Close()
url:="http://www.shop.com:8088/index/index/aa"
http.Post(url,contentType,buf)
}
三、Sling 库
1. 什么是Sling 库
https://github.com/dghubble/sling https://pkg.go.dev/github.com/dghubble/sling#section-readme
Sling是用于创建和发送API请求的Go HTTP客户端库。
项目开发中,发送http请求的场景,推荐使用Sling库。Sling本身是基于net/http来处理发送请求,同时做了较好的封装,既可以利用net/http的一些特性(如:httptrace),同时又不必关心net/http库的一些琐碎细节。
Sling的默认实现可以覆盖大部分对于http发送请求场景,同时可以通过实现Doer和ResponseDecoder接口来扩展个性化的场景。
Sling支持以下主要的功能:
- 支持GET/POST/PUT/PATCH/DELETE/HEAD
- 基于Base/Path可以扩展和复用Sling
- query参数可以用结构体来Encode
- Request Body支持form和json
- 可将Json格式的Response直接Decode到定义好的结构体
- 可扩展Response的Decoder,以及Doer。
Sling 存储HTTP请求属性以简化发送请求和解码响应。检查使用情况或示例以了解如何将Sling 组成API客户端。
由上使用案例,Sling 常被用来封装 http client sdk。
2. Sling使用方法
https://pkg.go.dev/github.com/dghubble/sling?utm_source=godoc 【GoCN酷Go推荐】灵活的Go http client库-Sling 参考URL: https://jishuin.proginn.com/p/763bfbd5b75f
Sling对http请求的要素method、baseUrl、Path、query、body、request、response等做了封装,基本使用可以参考https://github.com/dghubble/sling 上的示例代码。
可以通过实现ResponseDecoder和Doer的接口,来定制响应的decoder和发送请求的具体实现。
Sling拓展
每个Sling都会创建一个标准的http.request Request()调用(例如,使用某些路径和查询参数)。您可能希望扩展现有Sling以最大限度地减少重复(例如常见客户端或基本URL)。
每个Sling实例都提供了一种创建一个独立副本的New()方法,因此,子组的设置属性不会使父Sling变异。
const twitterApi = "https://api.twitter.com/1.1/"
base := sling.New().Base(twitterApi).Client(authClient)
tweetShowSling := base.New().Get("statuses/show.json").QueryStruct(params)
req, err := tweetShowSling.Request()
tweetPostSling := base.New().Post("statuses/update.json").BodyForm(params)
req, err := tweetPostSling.Request()
如果要扩展吊索,请使用New()创建一个新的子副本。
修改Request
Sling提供raw http.Request ,因此可以使用标准的 net/http 功能进行修改。
例如,在Go 1.7+中,将HTTP跟踪添加到具有上下文的请求:
req, err := sling.New().Get("https://example.com").QueryStruct(params).Request()
trace := &httptrace.ClientTrace{
DNSDone: func(dnsInfo httptrace.DNSDoneInfo) {
fmt.Printf("DNS Info: %+v\n", dnsInfo)
},
GotConn: func(connInfo httptrace.GotConnInfo) {
fmt.Printf("Got Conn: %+v\n", connInfo)
},
}
req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
client.Do(req)
post请求form-data demo
查看Sling 源码 得知,Sling 不直接支持 multipart/form-data
const (
contentType = "Content-Type"
jsonContentType = "application/json"
formContentType = "application/x-www-form-urlencoded"
)
提供的2个传内容的方法里面没有 处理 multipart/form-data 情况: 如下,一个是json,一个是在body中 进行url编码。
func (s *Sling) BodyJSON(bodyJSON interface{}) *Sling {
if bodyJSON == nil {
return s
}
return s.BodyProvider(jsonBodyProvider{payload: bodyJSON})
}
func (s *Sling) BodyForm(bodyForm interface{}) *Sling {
if bodyForm == nil {
return s
}
return s.BodyProvider(formBodyProvider{payload: bodyForm})
}
解决思路: Sling 本质还是使用标准库的http。Sling 封装的 不支持处理 multipart/form-data 情况。
亲测,尝试直接利用 Sling 失败,这里暂时使用标准http库的写法来实现。
读者如果直接利用Sling 可以实现,可以联系告诉我~!
用于构建一个API
APIs typically define an endpoint (also called a service) for each type of resource. For example, here is a tiny Github IssueService which lists repository issues.
APIs 通常为每种类型的资源定义端点(也称为服务)。例如,这是一个小型gihub IssueService,用于列出了github仓库issues。
const baseURL = "https://api.github.com/"
type IssueService struct {
sling *sling.Sling
}
func NewIssueService(httpClient *http.Client) *IssueService {
return &IssueService{
sling: sling.New().Client(httpClient).Base(baseURL),
}
}
func (s *IssueService) ListByRepo(owner, repo string, params *IssueListParams) ([]Issue, *http.Response, error) {
issues := new([]Issue)
githubError := new(GithubError)
path := fmt.Sprintf("repos/%s/%s/issues", owner, repo)
resp, err := s.sling.New().Get(path).QueryStruct(params).Receive(issues, githubError)
if err == nil {
err = githubError
}
return *issues, resp, err
}
使用Sling构建APIs示例
- Digits dghubble/go-digits
- GoSquared drinkin/go-gosquared
- Kala ajvb/kala
- Parse fergstar/go-parse
- Swagger Generator swagger-api/swagger-codegen
- Twitter dghubble/go-twitter
- Stacksmith jesustinoco/go-smith
由上知, twitter的client就是利用 Sling 封装的 go sdk, 项目参考 github.com/dghubble/go-twitter/twitter

四、参考
golang系列——实战http客户端 参考URL: https://zhuanlan.zhihu.com/p/134146738 【GoCN酷Go推荐】灵活的Go http client库-Sling 参考URL: https://jishuin.proginn.com/p/763bfbd5b75f
|