package request import ( "crypto/tls" "encoding/json" "net/http" "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 = 10 * 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, } } // NewClient 创建一个标准的 Resty 客户端 func NewClient(cfg *Config) *resty.Client { defaults := DefaultConfg() if cfg == nil { cfg = defaults } else { // 合并零值,避免调用方只覆盖部分字段时丢失默认超时和 User-Agent。 if cfg.Timeout <= 0 { cfg.Timeout = defaults.Timeout } if cfg.UserAgent == "" { cfg.UserAgent = defaults.UserAgent } } client := resty.New() client.SetHeader("User-Agent", cfg.UserAgent) client.SetTimeout(cfg.Timeout) client.SetRetryCount(3) client.SetTLSClientConfig(&tls.Config{ InsecureSkipVerify: !cfg.VerifySSL, }) if cfg.Proxy != "" { client.SetProxy(cfg.Proxy) } if cfg.Debug { client.SetDebug(true) client.SetDebugBodyLimit(DefaultDebugBody) 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) } 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 }