在日常开发中常常会用到 http 库来发送申请,就将罕用的申请办法封装了一个库,灵便又好用。这里用到了建造者模式,相似的应用场景都能够这样来实现:
具体实现
package requests
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
"time"
)
const (contentType = "Content-Type")
type HttpSend struct {
ctx context.Context // 上下文
client *http.Client // 客户端
request *http.Request // 申请体
url, httpProxy string // 申请门路
getValue interface{} // get 申请体
postValue interface{} // post 申请体
postFormValue url.Values // form-data post 申请体
header map[string]string // header
timeout time.Duration // 超时工夫
tryNum int // 重试次数
doNum int // 已重试次数
intervalTime time.Duration // 重试间隔时间
err error // 谬误
}
// 应用建造者模式初始化 HttpClient
func NewHttpClient(ctx context.Context, options ...HttpOptions) *HttpSend {client := new(HttpSend)
client.ctx = ctx
for i := 0; i < len(options); i++ {options[i](client)
}
client.NewClient(ctx)
if client.timeout != 0 {client.client.Timeout = client.timeout}
return client
}
// 实例化 HttpClient
func (h *HttpSend) NewClient(ctx context.Context) {h.client = new(http.Client)
if h.httpProxy != "" {
var proxyUrl *url.URL
proxyUrl, h.err = url.Parse(h.httpProxy)
if h.err != nil {log.Errorf(ctx, fmt.Sprintf("初始化 httpClient-httpProxy 解析谬误,error:%s", h.err))
}
h.client.Transport = &http.Transport{Proxy: http.ProxyURL(proxyUrl),
}
}
}
// 包装了重试的 do 办法
func (h *HttpSend) HttpDo() []byte {
var resp *http.Response
var body []byte
resp, h.err = h.client.Do(h.request)
if h.err == nil {log.Infof(h.ctx, fmt.Sprintf("HttpDo,resp:%+v, error:%+v", resp, h.err))
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {body, h.err = ioutil.ReadAll(resp.Body)
} else {h.err = errors.New(resp.Status)
}
} else {
for h.doNum < h.tryNum {
h.doNum++
log.Errorf(h.ctx, fmt.Sprintf("HttpDo,url:%s,resp:%+v, 重试次数:%d,error:%s", h.url, resp, h.doNum, h.err))
time.Sleep(h.intervalTime)
h.NewClient(h.ctx)
body = h.HttpDo()
return body
}
}
return body
}
// 发送 get 申请
func (h *HttpSend) HttpGet() ([]byte, error) {
if h.err != nil {return []byte{}, h.err}
defer func(t time.Time) {log.Info(h.ctx, fmt.Sprintf("took %v/s", time.Since(t).Seconds()))
}(time.Now())
log.Info(h.ctx, fmt.Sprintf("url:%s,request:%s", h.url, h.url))
h.request, h.err = http.NewRequest(http.MethodGet, h.url, nil)
if h.err != nil {return []byte{}, h.err}
h.setHeader()
var body []byte
body = h.HttpDo()
log.Infof(h.ctx, fmt.Sprintf("url:%s,response:%s,error:%s", h.url, string(body), h.err))
return body, h.err
}
// 发送 json 参数的 get 申请
func (h *HttpSend) HttpGetWithJson() ([]byte, error) {
if h.err != nil {return []byte{}, h.err}
getValue, err := json.Marshal(h.getValue)
if err != nil {log.Errorf(h.ctx, fmt.Sprintf("err:%s", err))
return nil, err
}
log.Infof(h.ctx, fmt.Sprintf("url:%s,request:%+v", h.url, h.getValue))
h.request, h.err = http.NewRequest(http.MethodGet, h.url, strings.NewReader(string(getValue)))
if _, ok := h.header[contentType]; !ok {h.request.Header.Set(contentType, "application/json")
}
h.setHeader()
var body []byte
body = h.HttpDo()
log.Infof(h.ctx, fmt.Sprintf("url:%s,response:%s,error:%s", h.url, string(body), h.err))
return body, h.err
}
// 发送 json 格局的 post 申请
func (h *HttpSend) HttpPost() ([]byte, error) {
if h.err != nil {return []byte{}, h.err}
postValue, err := json.Marshal(h.postValue)
if err != nil {log.Errorf(h.ctx, fmt.Sprintf("err:%s", err))
return nil, err
}
log.Infof(h.ctx, fmt.Sprintf("url:%s,request:%s", h.url, h.postValue))
h.request, h.err = http.NewRequest(http.MethodPost, h.url, strings.NewReader(string(postValue)))
if _, ok := h.header[contentType]; !ok {h.request.Header.Set(contentType, "application/json")
}
h.setHeader()
var body []byte
body = h.HttpDo()
log.Infof(h.ctx, fmt.Sprintf("url:%s,response:%s", h.url, string(body)))
return body, h.err
}
// 发送 form-data 的 post 申请
func (h *HttpSend) HttpPostForm() ([]byte, error) {
if h.err != nil {return []byte{}, h.err}
log.Infof(h.ctx, fmt.Sprintf("url:%s,postValue:%+v", h.url, h.postFormValue))
h.request, h.err = http.NewRequest(http.MethodPost, h.url, strings.NewReader(h.postFormValue.Encode()))
if h.err != nil {return []byte{}, h.err}
h.request.Header.Set(contentType, "application/x-www-form-urlencoded")
var body []byte
body = h.HttpDo()
log.Infof(h.ctx, fmt.Sprintf("send post success,body:%s,h.err:%s", string(body), h.err))
return body, h.err
}
// 设置 header
func (h *HttpSend) setHeader() {
for k, v := range h.header {h.request.Header.Set(k, v)
}
}
type HttpOptions func(*HttpSend)
// 设置申请门路
func UrlOptions(url string) HttpOptions {return func(client *HttpSend) {client.url = url}
}
// 设置 post 申请体
func PostValueOptions(postValue interface{}) HttpOptions {return func(client *HttpSend) {client.postValue = postValue}
}
// 设置 form-data 格局的 post 申请体
func PostFormValueOptions(postFormValue url.Values) HttpOptions {return func(client *HttpSend) {client.postFormValue = postFormValue}
}
// 设置 get 申请体
func GetValueOptions(getValue interface{}) HttpOptions {return func(client *HttpSend) {client.getValue = getValue}
}
// 设置超时工夫
func TimeOptions(timeout time.Duration) HttpOptions {return func(client *HttpSend) {client.timeout = timeout}
}
// 设置代理
func ProxyOptions(proxy string) HttpOptions {return func(client *HttpSend) {client.httpProxy = proxy}
}
// 设置申请头
func HeaderOptions(header map[string]string) HttpOptions {return func(client *HttpSend) {client.header = header}
}
// 设置重试参数
func TryOptions(tryNum int, intervalTime time.Duration) HttpOptions {return func(client *HttpSend) {
client.tryNum = tryNum
client.intervalTime = intervalTime
}
}
应用
data, err := requests.NewHttpClient(
ctx,
requests.UrlOptions(xxxUrl),
requests.PostValueOptions(postParams),
requests.TryOptions(2, time.Second*3)).
HttpPost()