Some checks failed
Build / build (push) Has been cancelled
CodeQL / Analyze (go) (push) Has been cancelled
build / Build (push) Has been cancelled
GitHub Actions Mirror / mirror_to_gitee (push) Has been cancelled
GitHub Actions Mirror / mirror_to_gitlab (push) Has been cancelled
Issue Close Require / issue-close-require (push) Has been cancelled
Issue Check Inactive / issue-check-inactive (push) Has been cancelled
146 lines
5.0 KiB
Go
146 lines
5.0 KiB
Go
package httphelper
|
||
|
||
import (
|
||
"bytes"
|
||
"encoding/json"
|
||
"fmt"
|
||
"io"
|
||
"net/http"
|
||
"time"
|
||
)
|
||
|
||
// HTTPClient 定义一个通用的 HTTP 客户端结构
|
||
type HTTPClient struct {
|
||
Client *http.Client // 底层的 http.Client 实例
|
||
BaseURL string // 基础 URL,所有请求将基于此 URL
|
||
Headers map[string]string // 默认请求头
|
||
}
|
||
|
||
// NewHTTPClient 创建一个新的 HTTPClient 实例
|
||
// timeout: 请求超时时间,例如 10 * time.Second
|
||
// baseURL: 基础 URL,例如 "https://api.example.com"
|
||
// defaultHeaders: 默认请求头,例如 {"Content-Type": "application/json"}
|
||
func NewHTTPClient(timeout time.Duration, baseURL string, defaultHeaders map[string]string) *HTTPClient {
|
||
return &HTTPClient{
|
||
Client: &http.Client{
|
||
Timeout: timeout,
|
||
},
|
||
BaseURL: baseURL,
|
||
Headers: defaultHeaders,
|
||
}
|
||
}
|
||
|
||
// applyHeaders 为请求应用默认和自定义请求头
|
||
func (c *HTTPClient) applyHeaders(req *http.Request, customHeaders map[string]string) {
|
||
// 应用默认请求头
|
||
for key, value := range c.Headers {
|
||
req.Header.Set(key, value)
|
||
}
|
||
// 应用自定义请求头(覆盖默认请求头)
|
||
for key, value := range customHeaders {
|
||
req.Header.Set(key, value)
|
||
}
|
||
}
|
||
|
||
// doRequest 执行实际的 HTTP 请求
|
||
// method: HTTP 方法 (GET, POST, PUT, DELETE等)
|
||
// path: 请求路径,将与 BaseURL 拼接
|
||
// requestBody: 请求体数据,如果为 GET/DELETE 请求则为 nil
|
||
// customHeaders: 自定义请求头,将覆盖默认请求头
|
||
// responseData: 用于存储响应数据的目标结构体(指针类型)
|
||
func (c *HTTPClient) doRequest(
|
||
method, path string,
|
||
requestBody interface{},
|
||
customHeaders map[string]string,
|
||
responseData interface{},
|
||
) error {
|
||
// 拼接完整的 URL
|
||
url := c.BaseURL + path
|
||
|
||
var reqBodyReader io.Reader
|
||
if requestBody != nil {
|
||
// 将请求体编码为 JSON
|
||
jsonBody, err := json.Marshal(requestBody)
|
||
if err != nil {
|
||
return fmt.Errorf("json marshal request body failed: %w", err)
|
||
}
|
||
reqBodyReader = bytes.NewBuffer(jsonBody)
|
||
}
|
||
|
||
// 创建新的 HTTP 请求
|
||
req, err := http.NewRequest(method, url, reqBodyReader)
|
||
if err != nil {
|
||
return fmt.Errorf("create http request failed: %w", err)
|
||
}
|
||
|
||
// 应用请求头
|
||
c.applyHeaders(req, customHeaders)
|
||
|
||
// 发送请求
|
||
resp, err := c.Client.Do(req)
|
||
if err != nil {
|
||
return fmt.Errorf("send http request failed: %w", err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
// 检查 HTTP 状态码
|
||
if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusBadRequest {
|
||
bodyBytes, _ := io.ReadAll(resp.Body) // 读取错误响应体
|
||
return fmt.Errorf("http request failed with status: %d, body: %s", resp.StatusCode, string(bodyBytes))
|
||
}
|
||
|
||
// 如果提供了 responseData,则解析响应体
|
||
if responseData != nil {
|
||
err = json.NewDecoder(resp.Body).Decode(responseData)
|
||
if err != nil {
|
||
return fmt.Errorf("json decode response body failed: %w", err)
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// Get 发送 GET 请求
|
||
// path: 请求路径
|
||
// customHeaders: 自定义请求头
|
||
// responseData: 用于存储响应数据的目标结构体(指针类型)
|
||
func (c *HTTPClient) Get(path string, customHeaders map[string]string, responseData interface{}) error {
|
||
return c.doRequest(http.MethodGet, path, nil, customHeaders, responseData)
|
||
}
|
||
|
||
// Post 发送 POST 请求
|
||
// path: 请求路径
|
||
// requestBody: 请求体数据
|
||
// customHeaders: 自定义请求头
|
||
// responseData: 用于存储响应数据的目标结构体(指针类型)
|
||
func (c *HTTPClient) Post(path string, requestBody interface{}, customHeaders map[string]string, responseData interface{}) error {
|
||
return c.doRequest(http.MethodPost, path, requestBody, customHeaders, responseData)
|
||
}
|
||
|
||
// Put 发送 PUT 请求
|
||
// path: 请求路径
|
||
// requestBody: 请求体数据
|
||
// customHeaders: 自定义请求头
|
||
// responseData: 用于存储响应数据的目标结构体(指针类型)
|
||
func (c *HTTPClient) Put(path string, requestBody interface{}, customHeaders map[string]string, responseData interface{}) error {
|
||
return c.doRequest(http.MethodPut, path, requestBody, customHeaders, responseData)
|
||
}
|
||
|
||
// Delete 发送 DELETE 请求
|
||
// path: 请求路径
|
||
// customHeaders: 自定义请求头
|
||
// responseData: 用于存储响应数据的目标结构体(指针类型)
|
||
func (c *HTTPClient) Delete(path string, customHeaders map[string]string, responseData interface{}) error {
|
||
// DELETE 请求通常没有请求体,但某些 RESTful API 可能支持
|
||
return c.doRequest(http.MethodDelete, path, nil, customHeaders, responseData)
|
||
}
|
||
|
||
// Patch 发送 PATCH 请求
|
||
// path: 请求路径
|
||
// requestBody: 请求体数据
|
||
// customHeaders: 自定义请求头
|
||
// responseData: 用于存储响应数据的目标结构体(指针类型)
|
||
func (c *HTTPClient) Patch(path string, requestBody interface{}, customHeaders map[string]string, responseData interface{}) error {
|
||
return c.doRequest(http.MethodPatch, path, requestBody, customHeaders, responseData)
|
||
}
|