http
go
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"net/url"
"strings"
"time"
)
type MyHttpClient struct {
client *http.Client
}
var httpClient = &MyHttpClient{
client: &http.Client{
Timeout: 5 * time.Second,
Transport: &http.Transport{
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
},
},
}
func GetHttpClient() *MyHttpClient {
return httpClient
}
type MyRequest struct {
Method string
Header http.Header
Body any
Query url.Values
AutoProcessingHttpStatus bool
}
type MyResponse struct {
StatusCode int
Header http.Header
Body []byte
}
func (m *MyHttpClient) reqJSON(ctx context.Context, urlStr string, reqStruct *MyRequest) (*MyResponse, error) {
if reqStruct == nil {
reqStruct = &MyRequest{}
}
// 处理 url
u, err := url.Parse(urlStr)
if err != nil {
return nil, err
}
// 处理 method
if reqStruct.Method == "" {
reqStruct.Method = "GET"
}
method := strings.ToUpper(reqStruct.Method)
if reqStruct.Query != nil && len(reqStruct.Query) > 0 {
u.RawQuery = reqStruct.Query.Encode()
}
// 处理 body
var body io.Reader
if method != "GET" && reqStruct.Body != nil {
jsonStr, err := json.Marshal(reqStruct.Body)
if err != nil {
return nil, err
}
body = bytes.NewBuffer(jsonStr)
}
// 创建请求
req, err := http.NewRequestWithContext(ctx, method, u.String(), body)
if err != nil {
return nil, err
}
// 处理 header
for k, v := range reqStruct.Header {
for _, vv := range v {
req.Header.Add(k, vv)
}
}
if body != nil && req.Header.Get("Content-Type") == "" {
req.Header.Set("Content-Type", "application/json")
}
// 发送请求
start := time.Now()
resp, err := m.client.Do(req)
duration := time.Since(start)
log.Printf("HTTP %s %s status=%d duration=%s err=%v",
method, u.String(), resp.StatusCode, duration, err)
if err != nil {
return nil, err
}
defer resp.Body.Close()
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if reqStruct.AutoProcessingHttpStatus {
if resp.StatusCode >= 400 {
return nil, fmt.Errorf("http error: %d, body: %s", resp.StatusCode, string(bodyBytes))
}
}
return &MyResponse{StatusCode: resp.StatusCode, Header: resp.Header, Body: bodyBytes}, nil
}