Featured image of post 简单的restful http client 包实现

简单的restful http client 包实现

a simple restful go http client package which is easy to use

一个restful http client 实现

主要是基于日常开发中遇到的常见http 请求需求,做了简单的封装,不做过度设计, 特点是:

  • 链式调用
  • 响应处理
  • 原始响应缓存
  • 易于复用

代码实现

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package httpx

import (
	"bytes"
	"encoding/json"
	"io"
	"net/http"

	"github.com/rs/zerolog/log"
)

type HttpClientX struct {
	header        http.Header
	query         map[string]string
	tmpBody       *bytes.Buffer
	tmpResp       *bytes.Buffer
	respStautCode int
	respErr       error
}

func NewHttpClientX() *HttpClientX {
	return &HttpClientX{
		header:  make(http.Header),
		query:   make(map[string]string),
		tmpBody: bytes.NewBuffer(nil),
		tmpResp: bytes.NewBuffer(nil),
	}
}

func (h *HttpClientX) SetHeader(k, v string) *HttpClientX {
	h.header.Set(k, v)
	return h
}

func (h *HttpClientX) ClearHeader() *HttpClientX {
	h.header = make(http.Header)
	return h
}
func (h *HttpClientX) SetContentType(v string) *HttpClientX {
	h.header.Set("Content-Type", v)
	return h
}
func (h *HttpClientX) AddHeader(k, v string) *HttpClientX {
	h.header.Add(k, v)
	return h
}
func (h *HttpClientX) SetAuthorization(v string) *HttpClientX {
	h.header.Set("Authorization", v)
	return h
}

func (h *HttpClientX) SetQuery(k, v string) *HttpClientX {
	h.query[k] = v
	return h
}

func (h *HttpClientX) ClearQuery() *HttpClientX {
	h.query = make(map[string]string)
	return h
}

func (h *HttpClientX) Post(url string, obj any) *HttpClientX {
	return h.request("Post", url, obj)
}

func (h *HttpClientX) Get(url string) *HttpClientX {
	return h.request("Get", url, nil)
}

func (h *HttpClientX) Put(url string, obj any) *HttpClientX {
	return h.request("Put", url, obj)
}

func (h *HttpClientX) Delete(url string, obj any) *HttpClientX {
	return h.request("Delete", url, obj)
}

func (h *HttpClientX) request(method, url string, obj any) *HttpClientX {
	var body io.Reader
	if obj != nil {
		h.tmpBody.Reset()
		buf, err := json.Marshal(obj)
		if err != nil {
			log.Error().Err(err).Msg("SetBody")
			return h
		}
		h.tmpBody.Write(buf)
		body = h.tmpBody
	}

	req, err := http.NewRequest(method, url, body)
	if err != nil {
		log.Error().Err(err).Msg(method)
		h.respErr = err
		return h
	}

	if h.header["Content-Type"] == nil {
		req.Header.Set("Content-Type", "application/json")
	}
	for k, v := range h.header {
		if len(v) == 1 {
			req.Header.Set(k, v[0])
		}
	}
	if len(h.query) > 0 {
		q := req.URL.Query()
		for k, v := range h.query {
			q.Set(k, v)
		}
		req.URL.RawQuery = q.Encode()
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Error().Err(err).Msg(method)
		h.respErr = err
		return h
	}
	defer resp.Body.Close()
	h.respStautCode = resp.StatusCode
	h.tmpResp.Reset()
	_, err = io.Copy(h.tmpResp, resp.Body)
	if err != nil {
		log.Error().Err(err).Msg(method)
		h.tmpResp.Reset()
		h.respErr = err
		return h
	}
	return h
}

// 应该是用指针对象
func (h *HttpClientX) Then(obj any) *HttpClientX {
	if h.respErr != nil {
		return h
	}
	if len(h.tmpResp.Bytes()) == 0 {
		return h
	}
	if err := json.Unmarshal(h.tmpResp.Bytes(), obj); err != nil {
		log.Error().Err(err).Msg("Then")
		h.respErr = err
	}
	return h
}

func (h *HttpClientX) Catch(errHandle func(error)) *HttpClientX {
	if h.respErr != nil {
		errHandle(h.respErr)
	}
	return h
}

func (h *HttpClientX) GetRawResp() string {
	return h.tmpResp.String()
}

func (h *HttpClientX) GetStatusCode() int {
	return h.respStautCode
}

使用注意

  1. 先设置 header 和 query
  2. 可以设置 Content-Typ
  3. 然后通过 POST/PUT/GET/DELETE 发起实际请求
  4. 通过 Then 和 Catch 来解析响应类型,这里只是简单的做了Json 解析,可以自行扩展
  5. 可以通过GetRawRespGetStatusCode 获取原始响应和 响应状态码,避免json 解析错误后丢失原始内容
Licensed under CC BY-NC-SA 4.0
往日已经不在,未来尚未开始
使用 Hugo 构建
主题 StackJimmy 设计