基于Go语言实现好用的HTTP接口请求requests
使用Go自带的net/http库可以发送各种HTTP请求。
然而各种类型请求发送方式有点不太一致,这里参考Python requests库的使用方式,简单封装了一下。
代码如下:
文件requests.go内容
package main
import (
"encoding/json"
"io"
"io/ioutil"
"net/http"
"net/url"
"strings"
"time"
)
// 请求结构体
type Request struct {
Method string `json:"method"` // 请求方法
Url string `json:"url"` // 请求url
Params map[string]string `json:"params"` // Query参数
Headers map[string]string `json:"headers"` // 请求头
Cookies map[string]string `json:"cookies"` // todo 处理 Cookies
Data map[string]string `json:"data"` // 表单格式请求数据
Json map[string]string `json:"json"` // JSON格式请求数据 todo 多层 嵌套
Files map[string]string `json:"files"` // todo 处理 Files
Raw string `json:"raw"` // 原始请求数据
}
// 响应结构体
type Response struct {
StatusCode int `json:"status_code"` // 状态码
Reason string `json:"reason"` // 状态码说明
Elapsed float64 `json:"elapsed"` // 请求耗时(秒)
Content [] byte `json:"content"` // 响应二进制内容
Text string `json:"text"` // 响应文本
Headers map[string]string `json:"headers"` // 响应头
Cookies map[string]string `json:"cookies"` // todo 添加响应Cookies
Request *Request `json:"request"` // 原始请求
}
// 处理请求方法
func (r *Request) getMethod() string{
return strings.ToUpper(r.Method) // 必须转为全部大写
}
// 组装URL
func (r *Request) getUrl() string {
if r.Params != nil{
urlValues := url.Values{}
Url, _ := url.Parse(r.Url) // todo 处理err
for key, value := range(r.Params){
urlValues.Set(key,value)
}
Url.RawQuery = urlValues.Encode()
return Url.String()
}
return r.Url
}
// 组装请求数据
func (r *Request) getData() io.Reader {
var reqBody string
if r.Raw != "" {
reqBody = r.Raw
}else if r.Data != nil{
urlValues := url.Values{}
for key, value := range(r.Data){
urlValues.Add(key,value)
}
reqBody = urlValues.Encode()
r.Headers["Content-Type"] = "application/x-www-form-urlencoded"
}else if r.Json != nil{
bytesData, _ := json.Marshal(r.Json)
reqBody = string(bytesData)
r.Headers["Content-Type"] = "application/json"
}
return strings.NewReader(reqBody)
}
// 添加请求头-需要在getData后使用
func (r *Request) addHeaders(req *http.Request){
if r.Headers != nil{
for key, value := range(r.Headers){
req.Header.Add(key, value)
}
}
}
// 准备请求
func (r *Request) prepare() *http.Request{
Method := r.getMethod()
Url := r.getUrl()
Data := r.getData()
req, _ := http.NewRequest(Method, Url, Data)
r.addHeaders(req)
return req
}
// 组装响应对象
func (r *Request) packResponse(res *http.Response, elapsed float64) Response {
var resp Response
resBody, _ := ioutil.ReadAll(res.Body)
resp.Content = resBody
resp.Text = string(resBody)
resp.StatusCode = res.StatusCode
resp.Reason = strings.Split(res.Status, " ")[1]
resp.Elapsed = elapsed
resp.Headers = map[string]string{}
for key, value := range(res.Header){
resp.Headers[key] = strings.Join(value, ";")
}
return resp
}
// 发送请求
func (r *Request) Send() Response {
req := r.prepare()
client := &http.Client{}
start := time.Now()
res, _ := client.Do(req) // todo 处理err
defer res.Body.Close()
elapsed := time.Since(start).Seconds()
return r.packResponse(res, elapsed)
}
使用方法如下:
文件requests_test.go内容
package main
import (
"fmt"
"testing"
)
// 发送GET 请求
func TestGet(t *testing.T){
r := Request{
Method: "GET",
Url: "https://httpbin.org/get?name=张三&age=12",
Headers: map[string]string{"Cookie": "abc", "Token": "123"}}
resp := r.Send()
fmt.Printf("状态码: %d\n", resp.StatusCode)
fmt.Printf("原因: %s\n", resp.Reason)
fmt.Printf("响应时间: %f秒\n", resp.Elapsed)
fmt.Printf("响应文本: %s\n", resp.Text)
}
// 发送GET 带单独Query参数请求
func TestGetWithParams(t *testing.T){
r := Request{
Method: "GET",
Url: "https://httpbin.org/get",
Params: map[string]string{"name": "张三", "age": "12"},
Headers: map[string]string{"Cookie": "abc", "Token": "123"}}
resp := r.Send()
fmt.Printf("状态码: %d\n", resp.StatusCode)
fmt.Printf("原因: %s\n", resp.Reason)
fmt.Printf("响应时间: %f秒\n", resp.Elapsed)
fmt.Printf("响应文本: %s\n", resp.Text)
}
// 发送POST 表单请求
func TestPostForm(t *testing.T){
r := Request{
Method: "POST",
Url: "https://httpbin.org/post",
Data: map[string]string{"name": "张三", "age": "12"},
Headers: map[string]string{"Cookie": "abc", "Token": "123"}}
resp := r.Send()
fmt.Printf("状态码: %d\n", resp.StatusCode)
fmt.Printf("原因: %s\n", resp.Reason)
fmt.Printf("响应时间: %f秒\n", resp.Elapsed)
fmt.Printf("响应文本: %s\n", resp.Text)
}
// 发送POST JSON请求
func TestPostJson(t *testing.T){
r := Request{
Method: "POST",
Url: "https://httpbin.org/post",
Json: map[string]string{"name": "张三", "age": "12"},
Headers: map[string]string{"Cookie": "abc", "Token": "123"}}
resp := r.Send()
fmt.Printf("状态码: %d\n", resp.StatusCode)
fmt.Printf("原因: %s\n", resp.Reason)
fmt.Printf("响应时间: %f秒\n", resp.Elapsed)
fmt.Printf("响应文本: %s\n", resp.Text)
}
也可以读取json文件并发送请求,示例如下:
文件data.json内容
{
"method": "post",
"url": "https://httpbin.org/post",
"data": {"name": "Kevin", "age": 12}
}
文件requests_test.go内容
// 读取JSON文件发送请求
func TestRequestFromJsonFile(t *testing.T) {
jsonFile, err := os.Open("data.json")
if err != nil {
fmt.Println("打开文件失败")
}
defer jsonFile.Close()
jsonData, err := ioutil.ReadAll(jsonFile)
if err != nil {
fmt.Println("读取JSON文件失败")
}
r := Request{
Params: map[string]string{},
Headers: map[string]string{},
Data: map[string]string{},
Json: map[string]string{},
Files: map[string]string{}}
err = json.Unmarshal(jsonData, &r)
if err != nil {
fmt.Printf("反序列化出错")
}
fmt.Println(r)
resp := r.Send()
fmt.Printf("状态码: %d\n", resp.StatusCode)
fmt.Printf("原因: %s\n", resp.Reason)
fmt.Printf("响应时间: %f秒\n", resp.Elapsed)
fmt.Printf("响应文本: %s\n", resp.Text)
}
参考:golang常用的http请求操作
传送门:asmcos版