// utils/http_client.go package utils import ( "bytes" "context" "crypto/tls" "encoding/json" "fmt" "io" "net/http" "net/url" "strings" "time" ) // 扩展 HTTPClient 接口 type HTTPClient interface { RequestJSON(ctx context.Context, method string, reqURL string, body interface{}) ([]byte, error) RequestWithProxy(ctx context.Context, method string, reqURL string, body interface{}, proxyURL string) ([]byte, error) } // 修改 httpClient 结构体 type httpClient struct { client *http.Client defaultProxy string // 新增默认代理配置 skipSSLVerify bool } func NewHttpClient(timeout time.Duration, isSkipSSLVerify bool, defaultProxy string) HTTPClient { baseTransport := &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: isSkipSSLVerify, }, MaxIdleConns: 100, IdleConnTimeout: 90 * time.Second, DisableCompression: true, } return &httpClient{ client: &http.Client{ Timeout: timeout, Transport: baseTransport, }, defaultProxy: defaultProxy, skipSSLVerify: isSkipSSLVerify, } } // 改进后的带代理请求方法 func (c *httpClient) RequestWithProxy(ctx context.Context, method string, reqURL string, body interface{}, proxyURL string) ([]byte, error) { // 自动补充协议前缀(支持多种代理协议) if !strings.Contains(proxyURL, "://") { proxyURL = "http://" + proxyURL fmt.Printf("自动补充HTTP协议前缀: %s\n", proxyURL) } // 校验并解析代理URL parsedProxy, err := url.Parse(proxyURL) if err != nil { return nil, fmt.Errorf("代理地址格式错误(需要包含协议,示例:socks5://127.0.0.1:1080 或 http://proxy.com): %w", err) } // 配置代理传输 proxyTransport := c.client.Transport.(*http.Transport).Clone() proxyTransport.Proxy = http.ProxyURL(parsedProxy) // 添加Transport配置验证 if proxyTransport.Proxy == nil { return nil, fmt.Errorf("代理配置未正确应用到Transport") } // 创建带缓存的客户端 proxyClient := &http.Client{ Transport: proxyTransport, Timeout: c.client.Timeout, } // 执行请求并处理错误 respData, err := c.doRequest(ctx, proxyClient, method, reqURL, body) if err != nil { return nil, fmt.Errorf("代理请求失败(%s): %w", proxyURL, err) } return respData, nil } // 修改方法签名 func (c *httpClient) RequestJSON(ctx context.Context, method string, reqURL string, body interface{}) ([]byte, error) { return c.doRequest(ctx, c.client, method, reqURL, body) } // 重命名并修改doPostRequest func (c *httpClient) doRequest(ctx context.Context, client *http.Client, method string, reqURL string, body interface{}) ([]byte, error) { var reqBody []byte if body != nil && method != "GET" { var err error if byteBody, ok := body.([]byte); ok { reqBody = byteBody } else { if reqBody, err = json.Marshal(body); err != nil { return nil, fmt.Errorf("JSON序列化失败: %w", err) } } } if method == "GET" && body != nil { if params, ok := body.(map[string]string); ok { query := url.Values{} for k, v := range params { query.Add(k, v) } reqURL = fmt.Sprintf("%s?%s", reqURL, query.Encode()) } } req, err := http.NewRequestWithContext(ctx, method, reqURL, bytes.NewReader(reqBody)) if err != nil { return nil, fmt.Errorf("创建请求失败: %w", err) } req.Header.Set("Content-Type", "application/json; charset=utf-8") req.Header.Set("Accept", "application/json") // 原有脱敏日志 // Logger.Printf("[HTTP请求] %s %s\nHeaders: %+v\nBody: %.100s", // method, // strings.ReplaceAll(reqURL, "access_token=[^\"]*", "access_token=***"), // req.Header, // string(reqBody)) resp, err := client.Do(req) if resp != nil { // 记录响应日志(脱敏处理) bodyBytes, _ := io.ReadAll(resp.Body) resp.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) // Logger.Printf("[HTTP响应] Status: %d\nHeaders: %+v\nBody: %.200s", // resp.StatusCode, // resp.Header, // string(bodyBytes)) } if err != nil { return nil, fmt.Errorf("网络请求失败: %w", err) } defer resp.Body.Close() if resp.StatusCode < 200 || resp.StatusCode >= 300 { Logger.WithField("status", resp.StatusCode).Warn("非成功状态码") return nil, fmt.Errorf("非成功状态码: %d", resp.StatusCode) } return io.ReadAll(resp.Body) }