...

基于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版

posted @ 2022-05-31 23:14  韩志超  阅读(1885)  评论(0编辑  收藏  举报