一、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/****")// 用url.values方式构造form-data参数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 errorlog.Fatal("生成请求失败!",err)return}//注意别忘了设置headerreq.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系列——实战http客户端
参考URL: https://zhuanlan.zhihu.com/p/134146738
golang的net/http包已经提供了强大了网络操作函数。
1. golang使用http client发起get请求示例
get请求:
package csdnimport ("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)}//利用ioutil包读取服务器返回的数据data, err := ioutil.ReadAll(res.Body)res.Body.Close()//一定要记得关闭连接if err != nil {log.Fatal(err)}log.Printf("%s", data)}
2. golang语言发送json格式的http请求
package mainimport ("bytes""encoding/json""fmt""log""net/http")type JsonParams struct {Content string `json:"content"`ActivityId string `json:"activityId"`Type string `json:"type"`}func main() {requestBody := new(bytes.Buffer)json.NewEncoder(requestBody).Encode(JsonParams{Content: "xxxxxxx",ActivityId: "111",})request, err := http.NewRequest("POST", "https://xxx.com", requestBody)if err != nil {log.Fatal(err)}request.Header.Set("content-type", "application/json;")//request.Header.Set("accept", "application/json, text/plain, */*")client := &http.Client{}resp, err := client.Do(request)if err != nil {log.Fatal("client.Do err:")log.Fatal(err)} else {body := &bytes.Buffer{}_, err := body.ReadFrom(resp.Body)if err != nil {log.Fatal(err)}resp.Body.Close()fmt.Println(resp.StatusCode)//fmt.Println(resp.Header)//fmt.Println(body)}}
3. Http请求返回结果处理
返回json结果是乱码问题
情况一:页面是,是GBK,不是UTF-8时
问题:
比如原来的代码是
resp, err := this.cleint.Get(url)body, err := ioutil.ReadAll(resp.Body)
此时body中读取到的是乱码。
解决办法:
引入"golang.org/x/text/encoding/simplifiedchinese"
只需增加一个reader
resp, err := this.cleint.Get(url)reader := simplifiedchinese.GB18030.NewDecoder().Reader(resp.Body)body, err := ioutil.ReadAll(reader)
此时,body中就是正确解码后的汉字。
情况二:accept-encoding的影响
accept-encoding
问题分析:
原因是内容被gzip压缩。
在网页请求的时候,为了减少网页请求所消耗的带宽,提高数据传输的速度,通常会把数据进行压缩传输,这里就需要用到’Accept-Encoding’,它的值’gzip, deflate, br’,这里的值代表的意思是数据压缩采用的编码方式。
通常我们还需要关注一个值,那就是响应头里面的’Content-Encoding’。
'Content-Encoding’是指服务端使用了哪种压缩方式传输数据给你,'Accept-Encoding’表示你发送请求时告诉服务器,我可以使用哪些编码解压缩你返回的数据。
服务端会根据你发送的’Accept-encoding’来决定用什么格式’Content-Encoding’压缩数据传输。
解决方法:
//request.Header.Set(“accept-encoding”, “gzip, deflate, br”)
request.Header.Set(“accept-encoding”, “”)
请求结果转化为Map
在 Go 中 Http 请求的返回结果为*http.Response 类型,Response.Body 类型为 io.Reader。
TODO
4. Golang http添加COOKIE
package mainimport ("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测试验证没有问题,再写到代码中。
5. 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包,我们可以很容易地构建出满足要求的包体。
5.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)}
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)。
New()
const twitterApi = "https://api.twitter.com/1.1/"base := sling.New().Base(twitterApi).Client(authClient)// statuses/show.json SlingtweetShowSling := base.New().Get("statuses/show.json").QueryStruct(params)req, err := tweetShowSling.Request()// statuses/update.json SlingtweetPostSling := 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()// handle errortrace := &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编码。
// BodyJSON sets the Sling's bodyJSON. The value pointed to by the bodyJSON// will be JSON encoded as the Body on new requests (see Request()).// The bodyJSON argument should be a pointer to a JSON tagged struct. See// https://golang.org/pkg/encoding/json/#MarshalIndent for details.func (s *Sling) BodyJSON(bodyJSON interface{}) *Sling {if bodyJSON == nil {return s}return s.BodyProvider(jsonBodyProvider{payload: bodyJSON})}// BodyForm sets the Sling's bodyForm. The value pointed to by the bodyForm// will be url encoded as the Body on new requests (see Request()).// The bodyForm argument should be a pointer to a url tagged struct. See// https://godoc.org/github.com/google/go-querystring/query for details.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