package httpClient import ( "bytes" "encoding/json" "errors" "io" "net/http" "net/url" "strings" "time" "go.uber.org/zap" ) const ( DefaultTimeout time.Duration = 5 // 默认超时时间5s ) type HttpClientOptions struct { Timeout *time.Duration // 单位:s Header *http.Header } // PostBody 包含转换后的 Reader 和推荐的 Content-Type type PostBody struct { Reader io.Reader ContentType string } func HttpGet(url string, params url.Values, opt *HttpClientOptions) ([]byte, error) { // 拼接参数到 URL finalURL := url + "?" + params.Encode() // 创建请求 req, err := http.NewRequest( http.MethodGet, finalURL, nil) if err != nil { zap.L().Error("NewRequest failed.", zap.String("err", err.Error())) return nil, err } // 设置 Header if opt != nil && opt.Header != nil { for key, values := range *opt.Header { for _, v := range values { req.Header.Add(key, v) } } } // 设置 Timeout timeout := DefaultTimeout if opt != nil && opt.Timeout != nil { timeout = *opt.Timeout } // 发送请求 client := &http.Client{Timeout: timeout * time.Second} resp, err := client.Do(req) if err != nil { zap.L().Error("Do Send failed.", zap.String("err", err.Error())) return nil, err } defer resp.Body.Close() return io.ReadAll(resp.Body) } // toPostBody 将任意 body 转换为 http POST 可用的 io.Reader func toPostBody(body any) (*PostBody, error) { if body == nil { return &PostBody{Reader: nil, ContentType: ""}, nil } switch v := body.(type) { case io.Reader: // 直接传 io.Reader return &PostBody{Reader: v}, nil case string: // 字符串默认 text/plain return &PostBody{Reader: strings.NewReader(v), ContentType: "text/plain"}, nil case []byte: // []byte 默认 application/octet-stream return &PostBody{Reader: bytes.NewReader(v), ContentType: "application/octet-stream"}, nil case url.Values: // 表单类型 application/x-www-form-urlencoded return &PostBody{ Reader: strings.NewReader(v.Encode()), ContentType: "application/x-www-form-urlencoded; charset=UTF-8", }, nil default: // 尝试序列化为 JSON jsonData, err := json.Marshal(v) if err != nil { return nil, errors.New("unsupported body type and failed to marshal to JSON") } return &PostBody{ Reader: bytes.NewReader(jsonData), ContentType: "application/json", }, nil } } func HttpPost(url string, body any, opt *HttpClientOptions) ([]byte, error) { // 转换body数据 payload, err := toPostBody(body) if err != nil { zap.L().Error("to PostBody failed.", zap.String("err", err.Error())) return nil, err } // 创建请求 req, err := http.NewRequest( http.MethodPost, url, payload.Reader) if err != nil { zap.L().Error("NewRequest failed.", zap.String("err", err.Error())) return nil, err } // 设置 Content-Type req.Header.Set("Content-Type", payload.ContentType) // 设置 Header if opt != nil && opt.Header != nil { for key, values := range *opt.Header { for _, v := range values { req.Header.Add(key, v) } } } // 设置 Timeout timeout := DefaultTimeout if opt != nil && opt.Timeout != nil { timeout = *opt.Timeout } // 发送请求 client := &http.Client{Timeout: timeout * time.Second} resp, err := client.Do(req) if err != nil { zap.L().Error("Do Send failed.", zap.String("err", err.Error())) return nil, err } defer resp.Body.Close() return io.ReadAll(resp.Body) }