目录
- 在 Golang 的net/http 标准库很强大,可以实现一下功能
- 实现 HTTP 请求客户端:可以通过 http.Client 对象和 http.NewRequest 方法发送 HTTP 请求。
- 实现 HTTP 服务器:可以通过 http.ListenAndServe 和 http.HandleFunc 方法构建简单的 HTTP 服务器。
- 实现 WebSocket 服务:net/http 提供了 http.HandlerFunc 类型,可以方便地实现 WebSocket 服务。
- 支持 HTTPS 加密协议:net/http 可以通过 http.ListenAndServeTLS 方法来支持 HTTPS 协议,保证网络安全性。
- 支持 Cookie:net/http 可以通过 http.Cookie 和 CookieJar 等类型来设置和管理 Cookie。
- 支持文件传输:net/http 可以通过 http.ServeFile 方法来实现基于 HTTP 的文件传输服务,支持断点续传等功能。
- net/http 客户端下几个常见的API总结
//发送一个 GET 请求并返回响应结果
func Get(url string) (resp *http.Response, err error)
//发送 POST 请求并返回响应结果
func Post(url, contentType string, body io.Reader) (resp *http.Response, err error)
//发送一个 POST 表单请求并返回响应结果
func PostForm(url string, data url.Values) (resp *http.Response, err error)
//发送指定类型的 HTTP 请求,并返回响应结果。可以使用该方法发送任意类型的 HTTP 请求,例如 GET、POST、PUT、DELETE 等
func Do(req *http.Request) (resp *http.Response, err error)
//发送一个 HEAD 请求并返回响应结果
func Head(url string) (resp *http.Response, err error)
//创建一个 HTTP 请求对象。可以通过传入请求方法、请求 URL 和请求体等参数来创建一个 HTTP 请求对象
func NewRequest(method, url string, body io.Reader) (*http.Request, error)
//创建一个上传文件的 HTTP 请求对象,并返回请求对象。可以通过传入 URL 地址、附加参数、文件参数名称和文件路径等参数来创建一个 HTTP 文件上传请求对象
func NewFileUploadRequest(url string, params map[string]string, paramName, path string) (*http.Request, error)
//定义重定向策略函数。可以传入一个自定义的重定向策略函数,用于处理 HTTP 请求重定向过程中的行为
func RedirectPolicyFunc(f func(*http.Request, []*http.Request) error) func(*http.Request, []*http.Request) error
//执行默认的重定向策略。在发送 HTTP 请求时,如果遇到 3xx 类型的 HTTP 响应,就需要执行重定向策略来决定是否继续重定向和如何重定向
func CheckRedirect(req *http.Request, via []*http.Request) error
//定义超时处理器。可以通过传入一个超时时间、一个提示消息和一个 HTTP 处理器等参数来创建一个超时处理器对象,用于控制 HTTP 请求的超时处理行为
func TimeoutHandler(h http.Handler, dt time.Duration, msg string) http.Handler
//创建一个带超时时间的 HTTP 客户端。可以通过传入时间参数来创建一个带有超时处理机制的 HTTP 客户端对象
func WithTimeout(timeout time.Duration) *http.Client
//创建一个 HTTP 客户端对象。可以通过传入自定义的 HTTP 客户端参数,如超时时间、传输协议类型等,来创建一个新的客户端对象
func NewClient(c *http.Client) *http.Client
//限制读取数据的最大字节数。可以使用该函数创建一个读取数据的流处理器,限制数据的读取数量,保证 Web 应用程序的安全性和稳定性
func MaxBytesReader(w http.ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser
//设置客户端 Cookie。可以使用该函数创建并设置一个 HTTP Cookie 对象,将其添加到 HTTP 响应头信息中,发送给客户端浏览器
func SetCookie(w http.ResponseWriter, cookie *http.Cookie)
//读取 HTTP 请求。可以通过传入一个 bufio.Reader 类型的参数,逐行读取 HTTP 请求,返回一个 HTTP 请求对象和一个错误信息
func ReadRequest(b *bufio.Reader) (*http.Request, error)
//获取 HTTP 请求的 User-Agent 信息。可以使用该函数获取客户端浏览器或者其他客户端工具的 User-Agent 信息,以便对请求进行分析和处理
func UserAgent(r *http.Request) string
//解析 HTTP 协议版本。可以使用该函数解析 HTTP 协议的版本字段,返回协议的主版本号和次版本号等信息
func ParseHTTPVersion(vers string) (major, minor int, ok bool)
- net/http 服务端下几个常见的API总结
//启动一个 HTTP 服务器并监听指定地址和端口号。可以通过传入 HTTP 处理器来指定服务器的请求处理方式
func ListenAndServe(addr string, handler http.Handler) error
//启动基于 TLS 协议的 HTTPS 服务器。可以使用该 API 创建一个支持 HTTPS 协议的 Web 服务器,并提供 SSL/TLS 安全加密传输服务
func ListenAndServeTLS(addr, certFile, keyFile string, handler http.Handler) error
//定义静态文件服务器。可以通过传入文件根目录等参数来创建一个 HTTP 静态文件服务器
func FileServer(root http.FileSystem) http.Handler
//定义 404 响应处理器。可以使用该 API 定义一个默认的 404 错误处理器,用于处理无法找到指定资源时的 HTTP 请求
func NotFound(w http.ResponseWriter, r *http.Request)
//定义重定向响应处理器。可以使用该 API 定义一个默认的重定向处理器,用于在 Web 应用程序中实现 HTTP 重定向行为
func Redirect(w http.ResponseWriter, r *http.Request, url string, code int)
//定义前缀删除处理器。可以使用该 API 创建一个新的 HTTP 处理器对象,并移除请求 URL 中指定的前缀部分,然后再将剩余的路径信息传递给路由映射器进行处理
func StripPrefix(prefix string, h http.Handler) http.Handler
//创建一个新的 HTTP 服务路由器对象。可以使用该函数创建一个新的 HTTP 服务路由器,将不同的 URL 地址映射到不同的 HTTP 处理器上
func NewServeMux() *http.ServeMux
//注册 HTTP 请求处理器。可以使用该函数将一个 HTTP 处理器注册到指定的 URL 地址上
func HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request))
//注册 HTTP 请求处理器。可以使用该函数将一个 HTTP 处理器注册到指定的 URL 地址上
func Handle(pattern string, handler http.Handler)
//默认的 404 响应处理器。可以使用该函数获取一个默认的 404 错误处理器,用于在 Web 应用程序中实现 404 错误页面的提示和处理
func NotFoundHandler() http.Handler
//定义前缀删除处理器。可以使用该函数创建一个新的 HTTP 处理器对象,并移除请求 URL 中指定的前缀部分,然后再将剩余的路径信息传递给路由映射器进行处理
func StripPrefix(prefix string, h http.Handler) http.Handler
一. HttpServer
- 先编写一个http服务器示例
- 实现HandlerFunc编写处理器函数(在下方""通过DefaultServeMux了解路由的注册流程"步骤中又讲为什么是实现HandlerFunc,也可以创建结构体实现ServeHTTP编写处理器)
- 通过http的HandleFunc函数注册接口,将处理器与接口绑定,也称为注册路由
- 监听指定地址端口
- 运行main方法启动服务
import (
"fmt"
"log"
"net/http"
)
//1.自定义处理器,固定格式包含两个入参http.ResponseWriter 与 r *http.Request
//http.ResponseWriter: 通过它将响应发送给客户端
//http.Request的结构指针: 通过它获取客户端传递过来的数据
func httpExecute(w http.ResponseWriter, r *http.Request) {
//将响应写入 http.ResponseWriter 中
fmt.Fprintf(w, "<h1>Hello, Web!</h1>")
}
func main() {
//2.通过内部http调用HandleFunc()注册一个http请求接口,也成为注册处理器,
//将执行业务的处理器与对应的接口路径进行绑定
//第一个参数是路由表达式,也就是请求路径
//第二个参数是一个函数类型,也就是真正处理请求的函数
http.HandleFunc("/hello", httpExecute)
//3.通过http调用ListenAndServe()方法监听8080端口(冒号前面不写代表localhost)
if err := http.ListenAndServe(":8080", nil); err != nil {
//当err不为空时,打印异常日志
log.Fatal(err)
}
}
- 此时在浏览器输入http://localhost:8080/hello 得到输出 hello world,以上代码重点可分为三块
编写请求处理函数(实现处理器接口)
httpExecute(w http.ResponseWriter, r *http.Request)
注册路由
http.HandleFunc("/hello", httpExecute)
监听端口启动服务
http.ListenAndServe(":8080", nil)
1. 创建多路复⽤器
- 虽然默认的多路复⽤器使⽤起来很⽅便,但是在⽣产环境中不建议使⽤。因为 DefaultServeMux 是⼀个全局变量,所有代码,包括第三⽅代码都可以修改它。 有些第三⽅代码会在 DefaultServeMux 注册⼀些处理器,这可能与我们注册的处理器冲突
- 代码示例
import (
"fmt"
"log"
"net/http"
"time"
)
func main() {
//1.创建多路复用器Mux
mux := http.NewServeMux()
//2.注册处理器(也就是请求接口)
mux.HandleFunc("/hello", helloFunc)
//3.创建服务器对象,指定当前端口号,并设置多路复用器
server := &http.Server{
Addr: ":8080",
Handler: mux, //注册处理器
//不是必须要的
ReadTimeout: 1 * time.Second, //设置请求接口的读超时时间
WriteTimeout: 1 * time.Second, //设置请求接口的写超时时间
}
//4.通过设置好端口,多路复用器的server启动监听
if err := server.ListenAndServe(); err != nil {
log.Fatal(err)
}
}
//业务接口
func helloFunc(w http.ResponseWriter, r *http.Request) {
//将响应写入 http.ResponseWriter 中
fmt.Fprintf(w, "<h1>Hello, Web!</h1>")
}
2. http.Request 请求参数相关
- 解释在上面编写示例时发现,在http接口的入参出有"http.ResponseWriter"与"*http.Request",接收与响应的参数都在这两个结构里
- 了解http.Request结构
1.Method : 表示客户端想要调⽤服务器的 HTTP 协议⽅法,例如GET/POST/PUT/DELETE 等
2. URL : 被称为统⼀资源标识符, 由两部分组成统⼀资源名称与统⼀资源定位符,在go中也是一个结构体
// src/net/http/request.go
type Request struct {
Method string //请求方法,例如GET,POST等等
URL *url.URL
Proto string //http协议版本例如HTTP1.1
ProtoMajor int //http协议版本,1
ProtoMinor int //http协议版本相关 1
Header Header //请求头
Body io.ReadCloser //请求体内容
ContentLength int //请求体的字节长度
// 省略⼀些字段...
}
URL的解释
//在go中的结构
// net/url/url.go
type URL struct {
Scheme string
Opaque string
User *Userinfo
Host string
Path string
RawPath string
RawQuery string
Fragment string
}
- URL注意点: 如果在接口同通过 *http.Request.URL 拿到URL后,如果想继续获取URL中的数据时,只能读到URL.Path与URL.RawQuery
- 我们可以通过net去获取url中的相关数据,假设前端发送get请求为"问 localhost:8080/url/posts?page=1&count=10#main ",后端可以
URL := &net.URL {
Scheme: "http",
Host: "example.com",
Path: "/posts",
RawQuery: "page=1&count=10",
Fragment: "main", }
//输出 http://example.com/posts?page=1&count=10#main
fmt.Println(URL.String())
Header 请求头
- 请求头解释
// src/net/http/header.go
type Header map[string][]string
2. 请求接口,获取请求头中的数据
func headerHandler(w http.ResponseWriter, r *http.Request) {
for key, value := range r.Header {
fmt.Fprintf(w, "%s: %v\n", key, value)
}
}
Body/Content-Length
- Content-Length 表示请求体的字节⻓度
- 请求体的内容可以从 Body 字段中读取。Body 字段是⼀个 io.ReadCloser 接⼝。在读取之后要关闭它,否则会有资源泄露
- 请求接口读取body
func bodyHandler(w http.ResponseWriter, r *http.Request) {
data := make([]byte, r.ContentLength)
r.Body.Read(data) // 忽略错误处理
//通过defer关闭
defer r.Body.Close()
//如果想客户端传来的请求体内容回传给客户端,还可以使⽤ io/ioutil下的ReadAll()
//data, _ := ioutil.ReadAll(r.Body)
fmt.Fprintln(w, string(data))
}
获取请求参数
- URL 键值对例如: key1=value1&key2=value2,通过http.Request中的URL中的RawQuery获取
- 例如:发送":8080/query?name=ls&age=20" 请求
func queryHandler(w http.ResponseWriter, r *http.Request) {
//返回"name=ls&age=20"
fmt.Fprintln(w, r.URL.RawQuery)
}
- 处理"x-www-form-urlencoded" 编码的请求体,注意点处理时⾸先调⽤请求的 ParseForm ⽅法解析,然后
从 Form 字段中取数据,Form 字段的类型 url.Values 底层实际上是 map[string][]string 。调⽤ ParseForm ⽅法之后,可
以使⽤ url.Values 的⽅法操作数据
func formHandler(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
fmt.Fprintln(w, r.Form)
}
- PostForm 字段,如果⼀个请求,同时有 URL 键值对和表单数据,只想获取表单数据,可以使⽤ PostForm 字段, 只会返回表单数据,不包括 URL 键值
- MultipartForm 字段,如果要处理上传的⽂件,必须使⽤ multipart/form-data 编码,也需要先解析后使⽤,使⽤ ParseMultipartForm ,之后从 MultipartForm 字段取值
func multipartFormHandler(w http.ResponseWriter, r *http.Request) {
r.ParseMultipartForm(1024)
fmt.Fprintln(w, r.MultipartForm)
fileHeader := r.MultipartForm.File["uploaded"][0]
file, err := fileHeader.Open()
if err != nil {
fmt.Println("Open failed: ", err)
return
}
data, err := ioutil.ReadAll(file)
if err == nil {
fmt.Fprintln(w, string(data))
}
}
- MultipartForm 包含两个 map 类型的字段,⼀个表示表单键值对,⼀个为上传的⽂件信息,使⽤表单中⽂件控件名获取 MultipartForm.File 得到通过该控件上传的⽂件,可以是多个。是 multipart.FileHeader 类型,通过该类型可以获取⽂件的各个属性。,注意点,该⽅式⽤来处理⽂件,为了安全, ParseMultipartForm ⽅法需要传⼀个参数,表示最⼤使⽤内存,避免上传的⽂件占⽤空间过⼤
- FormValue/PostFormValue :为了⽅便地获取值, net/http 包提供了 FormValue/PostFormValue ⽅法。它们在需要时会⾃动调⽤ ParseForm/ParseMultipartForm ⽅法,FormValue ⽅法返回请求的 Form 字段中指定键的值。如果同⼀个键对应多个值,那么返回第⼀个。如果需要获取全部值,直接使⽤ Form 字段。下⾯代码将返回 hello 对应的第⼀个值
fmt.Fprintln(w, r.FormValue("hello"))
- PostFormValue ⽅法返回请求的 PostForm 字段中指定键的值。如果同⼀个键对应多个值,那么返回第⼀个。如果需要获取全部值,直接使⽤ PostForm 字段
- 注意: 当编码被指定为 multipart/form-data 时, FormValue/PostFormValue 将不会返回任何值,
它们读取的是 Form/PostForm 字段,⽽ ParseMultipartForm 将数据写⼊ MultipartForm 字段
3. http.ResponseWriter 响应参数相关
- 在响应调用方时使用net/http 包中的ResponseWriter
// src/net/http/
type ReponseWriter interface {
Header() Header
Write([]byte) (int, error)
WriteHeader(statusCode int) }
- 接收请求后多路复⽤器会⾃动创建⼀个实现了http.ResponseWriter 接⼝的 http.response 对象,然后将该对象的指针和请求对象作为参数传给处理器,使⽤指针是为了能在处理逻辑中⽅便地获取请求信息
- ResponseWriter 有三个方法,Write(), WriteHeader(), Header(),通过这 3 个⽅法,响应调用方例如上面使用的 fmt.Fprintln(w, “Hello,
Web”) 其实底层调⽤了 Write ⽅法 - Write([]byte) (int, error) ⽅法, ResponseWriter不但实现了该方法并且也实现了 io.Writer 接⼝,在上面fmt.Fprintln 的第
⼀个参数接收⼀个 io.Writer 接⼝,这就是fmt.Fprintln可以正常执行的原因 - 也可以通过 ResponseWriter 直接调用Write()进行响应
func writeHandler(w http.ResponseWriter, r *http.Request) {
str := `<html>响应数据</html>`
w.Write([]byte(str))
}
WriteHeader ⽅法
- 注意点:WriteHeader ⽅法并不能⽤于设置响应⾸部,WriteHeader 接收⼀个整数,并将这个整数作为 HTTP 响应的状态码返回,调⽤这个返回之后,可以继续对 ResponseWriter 进⾏写⼊,但是不能对响应的⾸部进⾏任何修改操作。如果⽤户在调⽤ Write ⽅法之前没有执⾏过
WriteHeader ⽅法,那么程序默认会使⽤ 200 作为响应的状态码 - 通过WriteHeader 手动返回501示例
func writeHeaderHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(501)
fmt.Fprintln(w, "This API not implemented!!!")
}
Header ⽅法
- 解释: Header ⽅法其实返回的是⼀个 http.Header 类型,该类型的底层为 map[string][]string,该Header 类型中定义了 CRUD ⽅法,可以通过这些⽅法操作⾸部
// src/net/http/header.go
type Header map[string][]string
- 通过 Header 设置重定向
func headerHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Location", "http://baidu.com")
// 302 表示重定向
w.WriteHeader(302)
}
- 通过Header设置响应格式为JSON示例
func jsonHandler(w http.ResponseWriter, r *http.Request) {
//添加响应首部
w.Header().Set("Content-Type", "application/json")
//创建响应参数
u := &User {
FirstName: "ls",
LastName: "ls",
Age: 18,
Hobbies: []string{"reading", "learning"},
}
//将响应参数序列化为JSON
data, _ := json.Marshal(u)
//响应
w.Write(data)
}
4. cookie 相关
- Go 中 cookie 使⽤ net/http 包下的 http.Cookie 结构表示
// src/net/http/cookie.go
type Cookie struct {
Name string //键
Value string //值
Path string
Domain string
Expires time.Time //没有设置 Expires 字段的 cookie 被称为会话 cookie 或临时 cookie,关闭浏览器自动删除
RawExpires string
MaxAge int
Secure bool
HttpOnly bool //HttpOnly 字段设置为 true 时,该 cookie 只能通过 HTTP 访问,如JavaScript
SameSite SameSite
Raw string
Unparsed []string
}
//注意点:Expires 和 MaxAge 都可以⽤于设置 cookie 的过期时间。
//Expires 字段设置的是 cookie 在什么时间点过期,
//⽽ MaxAge 字段表示 cookie ⾃创建之后能够存活多少秒
// HTTP 1.1 中废弃了Expires ,推荐使⽤ MaxAge 代替。但是⼏乎所有的浏览器都仍然⽀持 Expires
- cookie 需要通过响应的⾸部发送给客户端,浏览器收到 Set-Cookie ⾸部时,会将其中的值解析成cookie 格式保存在浏览器中
func setCookie(w http.ResponseWriter, r *http.Request) {
c1 := &http.Cookie{
Name: "name",
Value: "lianshi",
HttpOnly: true,
}
c2 := &http.Cookie{
Name: "age",
Value: "18",
HttpOnly: true,
}
w.Header().Set("Set-Cookie", c1.String())
w.Header().Add("Set-Cookie", c2.String())
}
- 为了使⽤的便捷, net/http 包还提供了 SetCookie ⽅法
func setCookie2(w http.ResponseWriter, r *http.Request) {
c1 := &http.Cookie{
Name: "name",
Value: "lianshi",
HttpOnly: true,
}
c2 := &http.Cookie{
Name: "age",
Value: "18",
HttpOnly: true,
}
http.SetCookie(w, c1)
http.SetCookie(w, c2)
}
- 请求接口中读取cookie,注意点r.Header[“Cookie”] 返回⼀个切⽚,这个切⽚⼜包含了⼀个字符串,⽽这个字符串⼜包含了客户端发
送的任意多个 cookie。如果想要取得单个键值对格式的 cookie,就需要解析这个字符串。 为此, net/http 包在 http.Request 上提供了⼀些⽅法使我们更容易地获取 cookie
func getCookie2(w http.ResponseWriter, r *http.Request) {
//Cookie ⽅法返回以传⼊参数为键的 cookie,如果该 cookie 不存在,则返回⼀个错误
name, err := r.Cookie("name")
if err != nil {
fmt.Fprintln(w, "cannot get cookie of name")
}
//Cookies ⽅法返回客户端传过来的所有 cookie
cookies := r.Cookies()
fmt.Fprintln(w, name)
fmt.Fprintln(w, cookies)
}
二. HttpClient
1. 封装http客户端简单示例
- 封装客户端与发送http请求代码简单示例
func ClentServer() {
//1.创建客户端连接池
transport := &http.Transport{
//拨号配置上下文
DialContext: (&net.Dialer{
Timeout: 30 * time.Second, //连接超时时间
KeepAlive: 30 * time.Second, //探活时间
}).DialContext,
MaxIdleConns: 100, //最大空闲连接
IdleConnTimeout: 90 * time.Second, //空闲超时时间
TLSHandshakeTimeout: 10 * time.Second, //tls握手超时时间
ExpectContinueTimeout: 1 * time.Second, //100-continue状态码超时时间
}
//2.创建客户端
client := &http.Client{
Timeout: time.Second * 30, //请求超时时间
Transport: transport, //绑定连接池
}
//3.客户端发送get请求示例
clientSendGet(client)
//4.客户端发起post请求示例
clientSendPost(client)
}
func clientSendGet(client *http.Client) {
resp, err := client.Get("http://127.0.0.1:8080/test")
if err != nil {
// handle error
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
// handle error
}
fmt.Println(string(body))
}
func clientSendPost(client *http.Client) {
resp, err := client.Post("http://127.0.0.1:8080/test", "application/json", strings.NewReader("name=val"))
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))
}
- 几种发送请求的代码示例
// 发送GET请求
// url:请求地址
// response:请求返回的内容
func Get(url string) (response string, err error) {
client := http.Client{Timeout: 5 * time.Second}
resp, error := client.Get(url)
if error != nil {
return "", error
}
defer resp.Body.Close()
var buffer [512]byte
result := bytes.NewBuffer(nil)
for {
n, err := resp.Body.Read(buffer[0:])
result.Write(buffer[0:n])
if err != nil && err == io.EOF {
break
} else if err != nil {
return "", error
}
}
response = result.String()
return response, nil
}
func clientSendPost2(client *http.Client) {
resp, err := client.PostForm("http://127.0.0.1:8080/test", url.Values{"key": {"Value"}, "id": {"123"}})
if err != nil {
// handle error
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
// handle error
}
fmt.Println(string(body))
}
func httpDo() {
client := &http.Client{}
req, err := http.NewRequest("POST", "http://127.0.0.1:8080/testp", strings.NewReader("name=小明"))
if err != nil {
// handle error
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Cookie", "name=anny")
resp, err := client.Do(req)
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
// handle error
}
fmt.Println(string(body))
}
2. 生产上基于原生http发送post请求示例
//url:请求url
//data:请求参数
//返回string字符串,异常时error不为空
func AdapterPost(url string, data interface{}) (string, error) {
jsonStr, _ := json.Marshal(data)
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
if err != nil {
return "", err
}
req.Header.Add("Content-type", "application/json")
req.Header.Add("msgid", "ssss")
client := &http.Client{Timeout: 60 * time.Second}
resp, error := client.Do(req)
defer resp.Body.Close()
if error != nil {
return "", error
}
result, _ := ioutil.ReadAll(resp.Body)
ret := string(result)
return ret, nil
}
3. 生产上基于三方库发送请求示例
//使用三方库发送请求示例: github.com/go-resty/resty/v2
//method:请求方法
//url:请求url
//headers:请求头
//data:请求参数
//result:响应数据结构(获取到响应后直接转换为指定结构)
func DoSend(method, url string, headers map[string]string, data interface{}, result interface{}) error {
/*jsonByte, _ := json.Marshal(data)
dataStr := string(jsonByte)
log.TraceLog("", "AdapterReq|Post|0|%s|%s", url, dataStr)*/
client := resty.New()
client.SetTimeout(60 * time.Second)
request := client.R().
SetHeaders(headers).
SetBody(data).
SetResult(result)
var resp *resty.Response
var err error
if "GET" == method {
resp, err = request.Get(url)
} else {
resp, err = request.Post(url)
}
if err != nil {
return err
}
if !resp.IsSuccess() {
//errMsg := resp.RawResponse.Status
return nil //err
}
respJsonByte, err := json.Marshal(result)
if err != nil {
return err
}
respStr := string(respJsonByte)
fmt.Print(respStr)
return nil
}