1
头图
在日常开发中经常会用到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()

爆裂Gopher
20 声望11 粉丝

一篇文章讲明白一个知识点,每月更新,欢迎关注与交流。