Skip to content

🏠 Client 客户端

pkg/ipapi/client.go — SDK 的核心类型。

定义

go
type Client struct {
	HTTPClient   *http.Client          // 底层 HTTP 客户端
	BaseURL      string                // API 基地址
	APIKey       string                // API 密钥
	APIKeyMode   APIKeyMode            // 认证方式
	UserAgent    string                // User-Agent 头
	Retries      int                   // 重试次数
	RateLimiter  <-chan time.Time      // 速率限制通道
	Callback     string                // JSONP 回调名
	errorHandler func(error) error     // 自定义错误处理(非导出)
}

🎨 一图抵千言

Client 字段按职责分四组:传输层、认证、行为策略、回调。下图展示字段归属与默认值。

字段说明

字段类型默认值对应选项说明
HTTPClient*http.Client10s 超时 + 3 跳转限制WithCustomHTTPClient底层传输层
BaseURLstringhttps://ipapi.co/WithBaseURLAPI 基地址
APIKeystring""WithAPIKeyAPI 密钥,空则匿名
APIKeyModeAPIKeyModeAPIKeyHeaderWithAPIKeyQuery认证方式
UserAgentstringipapi-go-client/1.0WithUserAgentUA 头
Retriesint2WithRetries网络错误/5xx 重试次数
RateLimiter<-chan time.Timenil(不限流)WithRateLimiter限流令牌通道
Callbackstring""WithCallbackJSONP 回调名
errorHandlerfunc(error) errornilWithErrorHandler错误处理回调
HTTPClient.Timeouttime.Duration10sWithTimeout单请求超时(就地修改)

构造

go
client := ipapi.NewClient(opts ...ClientOption) *Client

详见 NewClient

方法

Client 暴露 6 个查询方法,详见 方法详解

内部方法(非导出)

方法职责
doRequest执行请求,含限流/重试/状态码映射
applyAuth注入认证与 JSONP 回调
setHeaders设置通用头
mapStatusCodeToErrorHTTP 状态码 → 错误
handleError统一错误出口

下面这张时序图展示一次 GetIPInfo 调用内部方法的协作视角:限流令牌、重试判定与错误出口如何串联。

⚠️ 4xx 不重试

IsRetryableError 仅对 ErrRateLimited || ErrServerError || ErrNotFound 返回真。429(限流)虽归类为 ErrRateLimited,但属于 4xx——SDK 不重试,留给调用方决定退避策略。

常量

基地址与超时

go
const (
	defaultBaseURL    = "https://ipapi.co/"
	defaultTimeout    = 10 * time.Second
	maxRedirects      = 3
	defaultRetryDelay = 500 * time.Millisecond
)

格式常量

go
type Format string

const (
	FormatJSON  Format = "json"
	FormatJSONP Format = "jsonp"
	FormatXML   Format = "xml"
	FormatCSV   Format = "csv"
	FormatYAML  Format = "yaml"
)

validFormats 是合法格式的白名单 map,供 ValidateFormat 使用。

认证模式

go
type APIKeyMode int

const (
	APIKeyHeader APIKeyMode = iota // 0,Bearer Header(默认)
	APIKeyQuery                    // 1,?key= 查询参数
)

错误哨兵值

go
var (
	ErrInvalidIP        = errors.New("invalid IP address")
	ErrInvalidField     = errors.New("invalid field name")
	ErrInvalidFormat    = errors.New("invalid response format")
	ErrRateLimited      = errors.New("API rate limit exceeded")
	ErrReservedIP       = errors.New("reserved IP address")
	ErrNotFound         = errors.New("resource not found")
	ErrServerError      = errors.New("server error")
	ErrUnexpectedData   = errors.New("unexpected response data")
	ErrMethodNotAllowed = errors.New("method not allowed")
	ErrInvalidKey       = errors.New("invalid API key")
)

详见 错误类型

下面这张状态图展示 HTTP 响应码到哨兵错误的映射决策视角:哪些码可重试、哪些直接终态。

📊 哪些错误会被重试?

IsRetryableError 判定为真的三类:ErrServerError(5xx)、ErrNotFound(404,可重试因可能是瞬时缓存未命中)、网络错误。其余哨兵值(含 ErrRateLimited/ErrInvalidKey)一律直接返回,不消耗重试配额。

线程安全

Client 无可变共享状态,可在多 goroutine 间复用。建议复用单例而非每次新建。

🚀 复用单例

Client 字段在请求过程中不会被修改(errorHandler 等在构造时定型),因此单个实例可安全并发。复用还能复用底层 HTTP 连接池,避免重复握手。

go
// 全局单例,启动时构造
var ipapiClient = ipapi.NewClient(ipapi.WithAPIKey(os.Getenv("IPAPI_KEY")))
🔍 为什么线程安全?
  • HTTPClient 自身是并发安全的(net/http 设计如此)。
  • RateLimiter 是只读接收通道 <-chan time.Time,多 goroutine 抢令牌天然安全。
  • APIKeyBaseURL 等配置字段在构造后不再被 SDK 内部修改。
  • 唯一可变的是 RetriesRateLimiter 等导出字段——若你要在运行期改,应自行加锁或避免这样做。

下一步

基于 MIT 许可证发布