中南一枝花 發表於 2021-1-25 20:05:00

Go - httpclient 常用操作

<h1 id="httpclient">httpclient</h1>
<h2 id="模块介绍">模块介绍</h2>
<p>httpclient 是基于 <code>net/http</code>&nbsp; 封装的 Go HTTP 客户端请求包,支持常用的请求方式、常用设置,比如:</p>
<ul>
<li>支持设置 Mock 信息</li>
<li>支持设置失败时告警</li>
<li>支持设置失败时重试</li>
<li>支持设置项目内部的 Trace</li>
<li>支持设置超时时间、Header 等</li>
</ul>
<h2 id="请求说明">请求说明</h2>
<table>
<thead>
<tr>
<th>方法名</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>httpclient.Get()</td>
<td>GET 请求</td>
</tr>
<tr>
<td>httpclient.Post()</td>
<td>POST 请求</td>
</tr>
<tr>
<td>httpclient.PostForm()</td>
<td>POST 请求,form 形式</td>
</tr>
<tr>
<td>httpclient.PostJSON()</td>
<td>POST 请求,json 形式</td>
</tr>
<tr>
<td>httpclient.PutForm()</td>
<td>PUT 请求,form 形式</td>
</tr>
<tr>
<td>httpclient.PutJSON()</td>
<td>PUT 请求,json 形式</td>
</tr>
<tr>
<td>httpclient.PatchForm()</td>
<td>PATCH 请求,form 形式</td>
</tr>
<tr>
<td>httpclient.PatchJSON()</td>
<td>PATCH 请求,json 形式</td>
</tr>
<tr>
<td>httpclient.Delete()</td>
<td>DELETE 请求</td>
</tr>
</tbody>
</table>
<h2 id="配置说明">配置说明</h2>
<table>
<thead>
<tr>
<th>配置项</th>
<th>配置方法</th>
</tr>
</thead>
<tbody>
<tr>
<td>设置 TTL 本次请求最大超时时间</td>
<td>httpclient.WithTTL(ttl time.Duration)</td>
</tr>
<tr>
<td>设置 Header 信息</td>
<td>httpclient.WithHeader(key, value string)</td>
</tr>
<tr>
<td>设置 Logger 信息</td>
<td>httpclient.WithLogger(logger *zap.Logger)</td>
</tr>
<tr>
<td>设置 Trace 信息</td>
<td>httpclient.WithTrace(t trace.T)</td>
</tr>
<tr>
<td>设置 Mock 信息</td>
<td>httpclient.WithMock(m Mock)</td>
</tr>
<tr>
<td>设置失败时告警</td>
<td>httpclient.WithOnFailedAlarm(alarmTitle string, alarmObject AlarmObject, alarmVerify AlarmVerify)</td>
</tr>
<tr>
<td>设置失败时重试</td>
<td>httpclient.WithOnFailedRetry(retryTimes int, retryDelay time.Duration, retryVerify RetryVerify)</td>
</tr>
</tbody>
</table>
<h3 id="设置-ttl">设置 TTL</h3>
<pre><code class="language-go">// 设置本次请求最大超时时间为 5s
httpclient.WithTTL(time.Second*5),
</code></pre>
<h3 id="设置-header-信息">设置 Header 信息</h3>
<p>可以调用多次进行设置多对 key-value 信息。</p>
<pre><code class="language-go">// 设置多对 key-value 信息,比如这样:
httpclient.WithHeader("Authorization", "xxxx"),
httpclient.WithHeader("Date", "xxxx"),
</code></pre>
<h3 id="设置-logger-信息">设置 Logger 信息</h3>
<p>传递的 logger 便于 httpclient 打印日志。</p>
<pre><code class="language-go">// 使用上下文中的 logger,比如这样:
httpclient.WithLogger(ctx.Logger()),
</code></pre>
<h3 id="设置-trace-信息">设置 Trace 信息</h3>
<p>传递的 trace 便于记录使用 httpclient 调用第三方接口的链路日志。</p>
<pre><code class="language-go">// 使用上下文中的 trace,比如这样:
httpclient.WithTrace(ctx.Trace()),
</code></pre>
<h3 id="设置-mock-信息">设置 Mock 信息</h3>
<pre><code class="language-go">// Mock 类型
type Mock func() (body []byte)

// 需实现 Mock 方法,比如这样:
func MockDemoPost() (body []byte) {
        res := new(demoPostResponse)
        res.Code = 1
        res.Msg = "ok"
        res.Data.Name = "mock_Name"
        res.Data.Job = "mock_Job"

        body, _ = json.Marshal(res)
        return body
}

// 使用时:
httpclient.WithMock(MockDemoPost),
</code></pre>
<p>传递的 Mock 方式便于设置调用第三方接口的 Mock 数据。只要约定了接口文档,即使对方接口未开发时,也不影响数据联调。</p>
<h3 id="设置失败时告警">设置失败时告警</h3>
<pre><code class="language-go">// alarmTitle 设置失败告警标题 String

// AlarmObject 告警通知对象,可以是邮件、短信或微信
type AlarmObject interface {
        Send(subject, body string) error
}

// 需要去实现 AlarmObject 接口,比如这样:
var _ httpclient.AlarmObject = (*AlarmEmail)(nil)

type AlarmEmail struct{}

func (a *AlarmEmail) Send(subject, body string) error {
        options := &amp;mail.Options{
                MailHost: "smtp.163.com",
                MailPort: 465,
                MailUser: "xx@163.com",
                MailPass: "",
                MailTo:   "",
                Subject:subject,
                Body:   body,
        }
        return mail.Send(options)
}

// AlarmVerify 定义符合告警的验证规则
type AlarmVerify func(body []byte) (shouldAlarm bool)

// 需要去实现 AlarmVerify 方法,比如这样:
func alarmVerify(body []byte) (shouldalarm bool) {
        if len(body) == 0 {
                return true
        }

        type Response struct {
                Code int `json:"code"`
        }
        resp := new(Response)
        if err := json.Unmarshal(body, resp); err != nil {
                return true
        }

    // 当第三方接口返回的 code 不等于约定的成功值(1)时,就要进行告警
        return resp.Code != 1
}

// 使用时:
httpclient.WithOnFailedAlarm("接口告警", new(third_party_request.AlarmEmail), alarmVerify),
</code></pre>
<h3 id="设置失败时重试">设置失败时重试</h3>
<pre><code class="language-go">// retryTimes 设置重试次数 Int,默认:3

// retryDelay 设置重试前延迟等待时间 time.Duration,默认:time.Millisecond * 100

// RetryVerify 定义符合重试的验证规则
type RetryVerify func(body []byte) (shouldRetry bool)

// 需要去实现 RetryVerify 方法,比如这样:
func retryVerify(body []byte) (shouldRetry bool) {
        if len(body) == 0 {
                return true
        }

        type Response struct {
                Code int `json:"code"`
        }
        resp := new(Response)
        if err := json.Unmarshal(body, resp); err != nil {
                return true
        }

    // 当第三方接口返回的 code 等于约定值(10010)时,就要进行重试
        return resp.Code = 10010
}

// RetryVerify 也可以为 nil , 当为 nil 时,默认重试规则为 http_code 为如下情况:
// http.StatusRequestTimeout, 408
// http.StatusLocked, 423
// http.StatusTooEarly, 425
// http.StatusTooManyRequests, 429
// http.StatusServiceUnavailable, 503
// http.StatusGatewayTimeout, 504

// 使用时:
httpclient.WithOnFailedRetry(3, time.Second*1, retryVerify),
</code></pre>
<h2 id="示例代码">示例代码</h2>
<pre><code class="language-go">// 以 httpclient.PostForm 为例

api := "http://127.0.0.1:9999/demo/post"
params := url.Values{}
params.Set("name", name)
body, err := httpclient.PostForm(api, params,
        httpclient.WithTTL(time.Second*5),
        httpclient.WithTrace(ctx.Trace()),
        httpclient.WithLogger(ctx.Logger()),
        httpclient.WithHeader("Authorization", "xxxx"),
        httpclient.WithMock(MockDemoPost),
    httpclient.WithOnFailedRetry(3, time.Second*1, retryVerify),
    httpclient.WithOnFailedAlarm("接口告警", new(third_party_request.AlarmEmail), alarmVerify),                           
)

if err != nil {
    return nil, err
}

res = new(demoPostResponse)
err = json.Unmarshal(body, res)
if err != nil {
    return nil, errors.Wrap(err, "DemoPost json unmarshal error")
}

if res.Code != 1 {
    return nil, errors.New(fmt.Sprintf("code err: %d-%s", res.Code, res.Msg))
}

return res, nil
</code></pre>
<blockquote>
<p>以上代码在 go-gin-api 项目中,地址:https://github.com/xinliangnote/go-gin-api</p>
</blockquote>


</div>
<div id="MySignature" role="contentinfo">
    <p style="text-align: center"><img src="https://img2020.cnblogs.com/blog/389840/202008/389840-20200822143814170-1389230174.png"></p>
<p style="border: #e7e7e7 1px solid; padding: 10px 10px; font-size: 12px; background-color: whitesmoke"><span style="margin-left: -14px">作者:新亮笔记(关注公众号,可申请添加微信好友)</span>
      <br>
      <span style="margin-left: 10px">出处:https://www.cnblogs.com/xinliangcoder</span>
      <br>
      <span style="margin-left: 10px; color: black">本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。</span>
</p><br><br>
来源:https://www.cnblogs.com/xinliangcoder/p/14327148.html
頁: [1]
查看完整版本: Go - httpclient 常用操作