Go - httpclient 常用操作

新亮筆記發表於2021-01-25

httpclient

模組介紹

httpclient 是基於 net/http  封裝的 Go HTTP 客戶端請求包,支援常用的請求方式、常用設定,比如:

  • 支援設定 Mock 資訊
  • 支援設定失敗時告警
  • 支援設定失敗時重試
  • 支援設定專案內部的 Trace
  • 支援設定超時時間、Header 等

請求說明

方法名 描述
httpclient.Get() GET 請求
httpclient.Post() POST 請求
httpclient.PostForm() POST 請求,form 形式
httpclient.PostJSON() POST 請求,json 形式
httpclient.PutForm() PUT 請求,form 形式
httpclient.PutJSON() PUT 請求,json 形式
httpclient.PatchForm() PATCH 請求,form 形式
httpclient.PatchJSON() PATCH 請求,json 形式
httpclient.Delete() DELETE 請求

配置說明

配置項 配置方法
設定 TTL 本次請求最大超時時間 httpclient.WithTTL(ttl time.Duration)
設定 Header 資訊 httpclient.WithHeader(key, value string)
設定 Logger 資訊 httpclient.WithLogger(logger *zap.Logger)
設定 Trace 資訊 httpclient.WithTrace(t trace.T)
設定 Mock 資訊 httpclient.WithMock(m Mock)
設定失敗時告警 httpclient.WithOnFailedAlarm(alarmTitle string, alarmObject AlarmObject, alarmVerify AlarmVerify)
設定失敗時重試 httpclient.WithOnFailedRetry(retryTimes int, retryDelay time.Duration, retryVerify RetryVerify)

設定 TTL

// 設定本次請求最大超時時間為 5s
httpclient.WithTTL(time.Second*5),

設定 Header 資訊

可以呼叫多次進行設定多對 key-value 資訊。

// 設定多對 key-value 資訊,比如這樣:
httpclient.WithHeader("Authorization", "xxxx"),
httpclient.WithHeader("Date", "xxxx"),

設定 Logger 資訊

傳遞的 logger 便於 httpclient 列印日誌。

// 使用上下文中的 logger,比如這樣:
httpclient.WithLogger(ctx.Logger()),

設定 Trace 資訊

傳遞的 trace 便於記錄使用 httpclient 呼叫第三方介面的鏈路日誌。

// 使用上下文中的 trace,比如這樣:
httpclient.WithTrace(ctx.Trace()),

設定 Mock 資訊

// 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),

傳遞的 Mock 方式便於設定呼叫第三方介面的 Mock 資料。只要約定了介面文件,即使對方介面未開發時,也不影響資料聯調。

設定失敗時告警

// 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 := &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),

設定失敗時重試

// 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),

示例程式碼

// 以 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

以上程式碼在 go-gin-api 專案中,地址:https://github.com/xinliangnote/go-gin-api