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 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系列——实战http客户端
参考URL: https://zhuanlan.zhihu.com/p/134146738
golang的net/http包已经提供了强大了网络操作函数。
1. golang使用http client发起get请求示例
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)
}
//利用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 main
import (
"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 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测试验证没有问题,再写到代码中。
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)
}
三、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)。
New()
const twitterApi = "https://api.twitter.com/1.1/"
base := sling.New().Base(twitterApi).Client(authClient)
// statuses/show.json Sling
tweetShowSling := base.New().Get("statuses/show.json").QueryStruct(params)
req, err := tweetShowSling.Request()
// statuses/update.json Sling
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()
// handle error
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编码。
// 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