Files
wk-backend/pkg/request/client.go
2026-04-03 14:24:29 +08:00

145 lines
3.5 KiB
Go

package request
import (
"crypto/tls"
"encoding/json"
"net/http"
"net/url"
"time"
"ckwk/pkg/log"
"go.uber.org/zap/zapcore"
"resty.dev/v3"
)
var (
NoRedirectClient *resty.Client
RestyClient *resty.Client
)
const (
DefaultUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36 Edg/140.0.0.0"
DefaultTimeout = 30 * time.Second
DefaultDebugBody = 4 * 1024
)
type Config struct {
Timeout time.Duration
Proxy string
Debug bool
UserAgent string
VerifySSL bool
}
func DefaultConfg() *Config {
return &Config{
Timeout: DefaultTimeout,
UserAgent: DefaultUserAgent,
VerifySSL: true,
Debug: false,
}
}
func normalizeConfig(cfg *Config) *Config {
defaults := DefaultConfg()
if cfg == nil {
return defaults
}
if cfg.Timeout <= 0 {
cfg.Timeout = defaults.Timeout
}
if cfg.UserAgent == "" {
cfg.UserAgent = defaults.UserAgent
}
return cfg
}
func buildTransport(cfg *Config) *http.Transport {
baseTransport, ok := http.DefaultTransport.(*http.Transport)
if !ok {
baseTransport = &http.Transport{}
}
transport := baseTransport.Clone()
transport.TLSClientConfig = &tls.Config{
InsecureSkipVerify: !cfg.VerifySSL,
}
transport.Proxy = http.ProxyFromEnvironment
if cfg.Proxy != "" {
proxyURL, err := url.Parse(cfg.Proxy)
if err == nil {
transport.Proxy = http.ProxyURL(proxyURL)
} else {
log.Warn("代理地址解析失败,已忽略")
}
}
return transport
}
func ApplyConfig(client *resty.Client, cfg *Config) {
cfg = normalizeConfig(cfg)
client.SetHeader("User-Agent", cfg.UserAgent)
client.SetTimeout(cfg.Timeout)
client.SetRetryCount(3)
client.SetTransport(buildTransport(cfg))
client.SetDebug(cfg.Debug)
client.SetDebugBodyLimit(DefaultDebugBody)
}
// NewClient 创建一个标准的 Resty 客户端
func NewClient(cfg *Config) *resty.Client {
client := resty.New()
client.OnDebugLog(func(debugLog *resty.DebugLog) {
fields := map[string]any{
"request": map[string]any{
"host": debugLog.Request.Host,
"uri": debugLog.Request.URI,
"method": debugLog.Request.Method,
"proto": debugLog.Request.Proto,
"header": log.SanitizeHeaders(debugLog.Request.Header),
"attempt": debugLog.Request.Attempt,
"body": log.SanitizeBody(debugLog.Request.Header.Get("Content-Type"), debugLog.Request.Body),
},
"response": map[string]any{
"statusCode": debugLog.Response.StatusCode,
"status": debugLog.Response.Status,
"proto": debugLog.Response.Proto,
"receivedAt": debugLog.Response.ReceivedAt.Format(time.RFC3339Nano),
"durationMs": debugLog.Response.Duration.Milliseconds(),
"size": debugLog.Response.Size,
"header": log.SanitizeHeaders(debugLog.Response.Header),
"body": log.SanitizeBody(debugLog.Response.Header.Get("Content-Type"), debugLog.Response.Body),
},
}
if debugLog.TraceInfo != nil {
if traceJSON, err := json.Marshal(debugLog.TraceInfo); err == nil {
fields["trace"] = json.RawMessage(traceJSON)
}
}
log.Capture(zapcore.DebugLevel, "resty", "outbound exchange", fields)
})
client.SetDebugLogFormatter(nil)
ApplyConfig(client, cfg)
return client
}
// NewNoRedirectClient 创建一个禁止重定向的客户端
func NewNoRedirectClient(cfg *Config) *resty.Client {
client := NewClient(cfg)
client.SetRedirectPolicy(
resty.RedirectPolicyFunc(func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}),
)
return client
}