apsara

package module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 27, 2026 License: MIT Imports: 17 Imported by: 0

README

Apsara SDK — 阿里云专有云通用 Go SDK

Go Reference

通用、轻量的阿里云专有云(Apsara V3.18.6)Go SDK,不定义任何 API 的业务 struct,仅提供认证、请求构建和响应反序列化的通用基础设施。

  • 通用:一个 Client 实例可调用任意产品的任意 API
  • 零耦合:不生成任何 API struct,响应用 map[string]any 或自定义类型接收
  • 函数选项模式NewClient(opts...),扩展不破坏兼容性
  • 凭证链:自动从环境变量加载,也可手动指定
  • 结构化错误ApsaraError 包含状态码、RequestId、业务错误码
  • 指数退避重试:可配置重试次数,内置 jitter
  • 响应元数据:通过 WithMeta 获取 RequestId、状态码、原始 Header
  • 专有云适配:内置 x-acs-organizationidx-acs-resourcegroupid 等 Header
  • 自签名证书:支持 WithInsecureSkipVerify,适配内网环境
  • 轻量依赖:零外部依赖:仅使用 Go 标准库

安装

go get github.com/gomodb/apsara

快速开始

方式一:手动指定凭证
package main

import (
    "context"
    "fmt"
    "os"

    "github.com/gomodb/apsara"
)

func main() {
    ctx := context.Background()

    client, err := apsara.NewClient(
        apsara.WithEndpoint("ecs.aliyuncs.com"),
        apsara.WithRegion("cn-hangzhou"),
        apsara.WithCredential(apsara.Credential{
            AccessKeyID:     os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"),
            AccessKeySecret: os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"),
        }),
    )
    if err != nil {
        panic(err)
    }

    var resp map[string]any
    err = client.Get(ctx, "DescribeInstances", "2014-05-26",
        map[string]string{"PageSize": "5"}, &resp)
    if err != nil {
        panic(err)
    }
    fmt.Printf("Total: %v\n", resp["TotalCount"])
}
方式二:从环境变量加载凭证
export APSARA_ACCESS_KEY_ID=your-access-key-id
export APSARA_ACCESS_KEY_SECRET=your-access-key-secret
export APSARA_SECURITY_TOKEN=your-sts-token   # STS 可选
export APSARA_ENDPOINT=ecs.aliyuncs.com
export APSARA_REGION_ID=cn-hangzhou
client, err := apsara.NewClient(  // 从环境变量自动读取
        apsara.WithTimeout(30*time.Second),
    )
方式三:专有云完整配置
client, err := apsara.NewClient(
    apsara.WithEndpoint("ecs.aliyuncs.com"),
    apsara.WithRegion("cn-hangzhou"),
    apsara.WithCredential(apsara.Credential{
        AccessKeyID:     "xxx",
        AccessKeySecret: "yyy",
    }),
    apsara.WithInsecureSkipVerify(true),   // 自签名证书
    apsara.WithOrganizationID("org-xxx"),
    apsara.WithResourceGroupID("rg-xxx"),
    apsara.WithCallerSource("my-app"),
    apsara.WithTimeout(30*time.Second),
    apsara.WithRetry(3),                   // 失败重试 3 次
)

调用 API

// GET 请求
client.Get(ctx, "DescribeInstances", "2014-05-26",
    map[string]string{"PageSize": "10"}, &result)

// POST 请求
client.Post(ctx, "CreateInstance", "2014-05-26",
    map[string]string{"ImageId": "centos_7_9_x64", "InstanceType": "ecs.g6.large"},
    &result)

// 获取响应元数据
var meta apsara.ResponseMeta
client.Get(ctx, "DescribeInstances", "2014-05-26", nil, &result, apsara.WithMeta(&meta))
fmt.Println("RequestId:", meta.RequestID)
fmt.Println("StatusCode:", meta.StatusCode)

错误处理

err := client.Get(ctx, "DescribeInstances", "2014-05-26", nil, &result)
if err != nil {
    var ae *apsara.ApsaraError
    if errors.As(err, &ae) {
        fmt.Printf("Status: %d\n", ae.StatusCode)
        fmt.Printf("RequestId: %s\n", ae.RequestID)
        fmt.Printf("ErrorCode: %s\n", ae.Code)
        fmt.Printf("Message: %s\n", ae.Message)
    } else {
        fmt.Printf("Network error: %v\n", err)
    }
}

各产品参数速查

文档 Endpoint 示例 Version
云服务器 ECS ecs.aliyuncs.com 2014-05-26
专有网络 VPC vpc.aliyuncs.com 2016-04-28
负载均衡 SLB slb.aliyuncs.com 2014-05-15
云数据库 RDS rds.aliyuncs.com 2014-08-15
云数据库 MongoDB mongodb.aliyuncs.com 2015-12-01
云数据库 Redis/Tair r-kvstore.aliyuncs.com 2015-01-01
RocketMQ rocketmq.aliyuncs.com 2019-01-01
消息队列 Kafka kafka.aliyuncs.com 2019-01-01
专有云 DNS dns.aliyuncs.com 2015-01-09
云服务总线 CSB csb.aliyuncs.com 2017-11-18

API 参考

创建 Client
func NewClient(opts ...ClientOption) (*Client, error)
ClientOption
选项 说明 环境变量替代
WithEndpoint(s) API 服务地址(必填) APSARA_ENDPOINT
WithRegion(s) 地域 ID(必填) APSARA_REGION_ID
WithCredential(c) 访问凭证 APSARA_ACCESS_KEY_ID / APSARA_ACCESS_KEY_SECRET / APSARA_SECURITY_TOKEN
WithInsecureSkipVerify(b) 跳过 TLS 验证
WithHTTPClient(cl) 自定义 HTTP 客户端
WithOrganizationID(s) 组织 ID
WithResourceGroupID(s) 资源集 ID
WithInstanceID(s) 实例 ID
WithCallerSource(s) 调用来源标识
WithLogger(l) 日志记录器
WithRetry(n) 最大重试次数
WithTimeout(d) 单次 HTTP 请求总超时
RequestOption
选项 说明
WithMeta(m *ResponseMeta) 获取响应元数据(RequestId、状态码、Header、原始 Body)
错误类型
type ApsaraError struct {
    Action     string
    StatusCode int
    RequestID  string
    Code       string
    Message    string
    Err        error
}

设计参考

本 SDK 的设计参考了 AWS SDK v2 的关键模式:

  • 函数选项模式(Functional Options):NewClient(opts...) 可扩展不破坏签名
  • 凭证链(Credential Chain):环境变量 → 手动凭证
  • 结构化错误(Structured Error):ApsaraError 类似 AWS 的 smithy.RequestError
  • 指数退避重试(Exponential Backoff + Jitter):内置 backoff() 函数
  • 响应元数据(Response Metadata):通过 WithMeta 选项注入

许可证

MIT

Documentation

Overview

Package apsara 是阿里云专有云 (Apsara V3.18.6) 通用 Go SDK。

该 SDK 不定义各接口的 struct,仅提供认证、请求构建和响应序列化的通用基础设施。 一个 Client 实例可复用于所有产品,每次调用时传入 action(操作名)和 version(API 版本号)。

快速开始

import "github.com/gomodb/apsara"

client, err := apsara.NewClient(
    apsara.WithEndpoint("ecs.aliyuncs.com"),
    apsara.WithRegion("cn-hangzhou"),
    apsara.WithCredential(apsara.Credential{
        AccessKeyID:     os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"),
        AccessKeySecret: os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"),
    }),
    apsara.WithTimeout(30 * time.Second),
)
if err != nil { panic(err) }

var resp map[string]any
err = client.Get(ctx, "DescribeInstances", "2014-05-26",
    map[string]string{"PageSize": "10"}, &resp)

从环境变量加载凭证

export APSARA_ACCESS_KEY_ID=xxx
export APSARA_ACCESS_KEY_SECRET=yyy
export APSARA_SECURITY_TOKEN=zzz  # STS 可选

client, err := apsara.NewClient(
    apsara.WithEndpoint("ecs.aliyuncs.com"),
    apsara.WithRegion("cn-hangzhou"),
    apsara.WithTimeout(30 * time.Second),
)

专有云完整配置(超时 + 自签名证书 + 重试 + 组织/资源集)

client, err := apsara.NewClient(
    apsara.WithEndpoint("ecs.aliyuncs.com"),
    apsara.WithRegion("cn-hangzhou"),
    apsara.WithTimeout(30 * time.Second),
    apsara.WithInsecureSkipVerify(true),
    apsara.WithOrganizationID("org-xxx"),
    apsara.WithResourceGroupID("rg-xxx"),
    apsara.WithRetry(3),
)

错误处理

var ae *apsara.ApsaraError
if errors.As(err, &ae) {
    fmt.Printf("status=%d request=%s code=%s msg=%s\n",
        ae.StatusCode, ae.RequestID, ae.Code, ae.Message)
}

获取响应元数据

var meta apsara.ResponseMeta
err = client.Get(ctx, "DescribeInstances", "2014-05-26", nil, &resp,
    apsara.WithMeta(&meta))
fmt.Println("RequestId:", meta.RequestID)
Example
package main

import (
	"context"
	"errors"
	"fmt"
	"time"

	"github.com/gomodb/apsara"
)

func main() {
	ctx := context.Background()

	// 创建客户端(超时 + 自签名证书 + 重试)
	client, err := apsara.NewClient(
		apsara.WithEndpoint("ecs.aliyuncs.com"),
		apsara.WithRegion("cn-hangzhou"),
		apsara.WithCredential(apsara.Credential{
			AccessKeyID:     "your-access-key-id",
			AccessKeySecret: "your-access-key-secret",
		}),
		apsara.WithTimeout(30*time.Second),  // 请求超时 30s
		apsara.WithInsecureSkipVerify(true), // 自签名证书
		apsara.WithOrganizationID("org-xxx"),
		apsara.WithResourceGroupID("rg-xxx"),
		apsara.WithRetry(3), // 最多重试 3 次
	)
	if err != nil {
		fmt.Printf("create client: %v\n", err)
		return
	}

	// 调用 ECS API,附带获取元数据
	var (
		resp map[string]any
		meta apsara.ResponseMeta
	)

	err = client.Get(ctx,
		"DescribeInstances", "2014-05-26",
		map[string]string{"PageSize": "5"},
		&resp,
		apsara.WithMeta(&meta),
	)
	if err != nil {
		var ae *apsara.ApsaraError
		if errors.As(err, &ae) {
			fmt.Printf("API error: status=%d request=%s code=%s\n",
				ae.StatusCode, ae.RequestID, ae.Code)
		} else {
			fmt.Printf("Error: %v\n", err)
		}

		return
	}

	fmt.Printf("RequestId: %s\n", meta.RequestID)
	fmt.Printf("Total: %v\n", resp["TotalCount"])

	// 同一 client 调用 VPC(需不同 endpoint 时新建 client)
	vpcClient, _ := apsara.NewClient(
		apsara.WithEndpoint("vpc.aliyuncs.com"),
		apsara.WithRegion("cn-hangzhou"),
		apsara.WithCredential(apsara.Credential{
			AccessKeyID:     "your-access-key-id",
			AccessKeySecret: "your-access-key-secret",
		}),
	)

	var vpcResp map[string]any

	err = vpcClient.Get(ctx, "DescribeVpcs", "2016-04-28", nil, &vpcResp)
	if err != nil {
		fmt.Printf("VPC Error: %v\n", err)
		return
	}

	fmt.Printf("Vpcs: %v\n", vpcResp["Vpcs"])
}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ApsaraError

type ApsaraError struct {
	// Action 是请求的 API 操作名称。
	Action string
	// StatusCode 是 HTTP 响应状态码。0 表示网络层错误。
	StatusCode int
	// RequestID 是阿里云请求的唯一标识,可从失败响应的 JSON 中提取。
	RequestID string
	// Code 是阿里云业务错误码,例如 "InvalidInstanceId"。
	Code string
	// Message 是阿里云业务错误描述。
	Message string
	// Err 是原始错误(网络层错误时不为 nil)。
	Err error
}

ApsaraError 是 SDK 返回的结构化错误。

StatusCode 含义:

  • 0:网络层错误(连接超时、DNS 解析失败等),Err 包含原始错误;
  • 400~599:服务端返回的错误,RequestID / Code / Message 从 JSON body 中解析。

func (*ApsaraError) Error

func (e *ApsaraError) Error() string

func (*ApsaraError) Unwrap

func (e *ApsaraError) Unwrap() error

Unwrap 返回原始错误,使 errors.As 可穿透到 ApsaraError。

type Client

type Client struct {
	// Endpoint 是 API 服务地址,例如 "ecs.aliyuncs.com"(不含 scheme 和 path)。
	Endpoint string
	// RegionID 是地域 ID,例如 "cn-hangzhou"。
	RegionID string
	// Scheme 是协议,默认为 "https"。
	Scheme string

	// HTTPClient 是自定义 HTTP 客户端。
	HTTPClient *http.Client
	// InsecureSkipVerify 为 true 时跳过 TLS 证书验证。
	InsecureSkipVerify bool

	// OrganizationID 对应 Header x-acs-organizationid。
	OrganizationID string
	// ResourceGroupID 对应 Header x-acs-resourcegroupid。
	ResourceGroupID string
	// InstanceID 对应 Header x-acs-instanceid。
	InstanceID string
	// CallerSource 对应 Header x-acs-caller-sdk-source。
	CallerSource string
	// contains filtered or unexported fields
}

Client 是阿里云专有云的通用 API 客户端。 一个 Client 实例可复用于任意产品,每次调用时传入 Action + Version。

func NewClient

func NewClient(opts ...ClientOption) (*Client, error)

NewClient 创建通用 API 客户端。

必填选项:WithEndpoint + WithRegion。 凭证可通过 WithCredential 手动设置,或通过环境变量自动加载。

使用示例:

client, err := apsara.NewClient(
    apsara.WithEndpoint("ecs.aliyuncs.com"),
    apsara.WithRegion("cn-hangzhou"),
)

func (*Client) BuildSignedParams

func (c *Client) BuildSignedParams(
	action, version string,
	bizParams map[string]string,
) (map[string]string, error)

BuildSignedParams 生成包含签名在内的完整查询参数映射。 返回的 map 可直接拼接到 URL 查询字符串中使用。

func (*Client) BuildURL

func (c *Client) BuildURL(action, version string, bizParams map[string]string) (string, error)

BuildURL 将带签名的参数构建为完整的请求 URL 字符串。

func (*Client) Delete

func (c *Client) Delete(
	ctx context.Context,
	action, version string,
	bizParams map[string]string,
	result any,
	opts ...RequestOption,
) error

Delete 发送 DELETE 请求。参数同 Get。

func (*Client) Do

func (c *Client) Do(ctx context.Context, req *http.Request, result any) error

Do 对已构建的 *http.Request 注入公共 Header、发送请求,并将 JSON 响应反序列化到 result。 此方法不校验 HTTP 状态码,也不解析业务错误,适合完全自定义的请求场景。

func (*Client) Get

func (c *Client) Get(
	ctx context.Context,
	action, version string,
	bizParams map[string]string,
	result any,
	opts ...RequestOption,
) error

Get 发送 GET 请求。

action:    API 操作名称(如 "DescribeInstances")
version:   API 版本号(如 "2014-05-26")
bizParams: 业务参数,可为 nil
result:    用于接收响应的值(需传入指针)
opts:      额外选项(如 WithMeta)

func (*Client) MustBuildURL

func (c *Client) MustBuildURL(action, version string, bizParams map[string]string) string

MustBuildURL 类似 BuildURL,但失败时 panic。适用于静态初始化。

func (*Client) Post

func (c *Client) Post(
	ctx context.Context,
	action, version string,
	bizParams map[string]string,
	result any,
	opts ...RequestOption,
) error

Post 发送 POST 请求。参数同 Get。

func (*Client) Put

func (c *Client) Put(
	ctx context.Context,
	action, version string,
	bizParams map[string]string,
	result any,
	opts ...RequestOption,
) error

Put 发送 PUT 请求。参数同 Get。

func (*Client) RawRequest

func (c *Client) RawRequest(
	ctx context.Context,
	method string,
	params map[string]string,
) (*http.Response, error)

RawRequest 使用已签名的参数直接发送 HTTP 请求,并返回原始响应。 调用方需自行关闭 resp.Body 并解析响应。

type ClientOption

type ClientOption func(*Client)

ClientOption 是 NewClient 的配置选项。

func WithCallerSource

func WithCallerSource(source string) ClientOption

WithCallerSource 设置调用来源标识,对应 Header x-acs-caller-sdk-source。 未设置时默认使用 "apsara-go"。

func WithCredential

func WithCredential(cred Credential) ClientOption

WithCredential 手动设置访问凭证。 若未调用此选项,SDK 会依次尝试从环境变量 APSARA_ACCESS_KEY_ID / APSARA_ACCESS_KEY_SECRET / APSARA_SECURITY_TOKEN 加载。

func WithEndpoint

func WithEndpoint(endpoint string) ClientOption

WithEndpoint 设置 API 服务地址(必填)。 环境变量 APSARA_ENDPOINT 可替代此选项。

func WithHTTPClient

func WithHTTPClient(cl *http.Client) ClientOption

WithHTTPClient 设置自定义 HTTP 客户端。 设置后 InsecureSkipVerify 不生效,需自行在 Transport 中配置。

func WithInsecureSkipVerify

func WithInsecureSkipVerify(skip bool) ClientOption

WithInsecureSkipVerify 跳过 TLS 证书验证,适用于自签名证书的内网环境。

func WithInstanceID

func WithInstanceID(id string) ClientOption

WithInstanceID 设置实例 ID,对应 Header x-acs-instanceid。

func WithLogger

func WithLogger(l Logger) ClientOption

WithLogger 设置日志记录器。设置后 SDK 会在每次 API 调用时输出请求方法和 URL。

func WithOrganizationID

func WithOrganizationID(id string) ClientOption

WithOrganizationID 设置专有云的组织 ID,对应 Header x-acs-organizationid。

func WithRegion

func WithRegion(regionID string) ClientOption

WithRegion 设置地域 ID(必填)。 环境变量 APSARA_REGION_ID 可替代此选项。

func WithResourceGroupID

func WithResourceGroupID(id string) ClientOption

WithResourceGroupID 设置专有云的资源集 ID,对应 Header x-acs-resourcegroupid。

func WithRetry

func WithRetry(maxAttempts int) ClientOption

WithRetry 设置请求失败时的最大重试次数。 设置为 0 或 1 表示不重试(仅执行一次请求)。 重试退避采用指数退避 + 随机抖动,仅对网络错误和 5xx/429 响应生效。

func WithTimeout

func WithTimeout(d time.Duration) ClientOption

WithTimeout 设置单次 HTTP 请求的超时时间(含连接、TLS 握手、发送请求、读取响应体)。 默认为 0(不限时),受 context 约束。

type Credential

type Credential struct {
	AccessKeyID     string
	AccessKeySecret string
	// SecurityToken 仅在 STS 临时凭证时需要填写。
	SecurityToken string
}

Credential 表示阿里云 API 的访问凭证。 支持 AccessKey 和 STS 两种认证方式。

type Logger

type Logger interface {
	Logf(format string, args ...any)
}

Logger 是 SDK 的日志接口。实现此接口的类型可接收 SDK 的内部日志。 通过 WithLogger 注入。

type RequestOption

type RequestOption func(*requestConfig)

RequestOption 是 Get / Post / Put / Delete 函数的额外选项。

func WithMeta

func WithMeta(m *ResponseMeta) RequestOption

WithMeta 让 SDK 将响应元数据(状态码、Header、RequestId、原始 Body)写入 m。

func WithRequest added in v0.3.0

func WithRequest(req **http.Request) RequestOption

WithRequest 让 SDK 将最终的 *http.Request(含 URL、Header、Method)写入 req。 该请求对象在发起 HTTP 调用前捕获,可用于调试或记录完整的请求信息。 注意:req.Body 始终为 nil(所有参数均在 URL query string 中)。

type ResponseMeta

type ResponseMeta struct {
	StatusCode int
	Header     http.Header
	RequestID  string
	RawBody    []byte // 原始 JSON 响应体
}

ResponseMeta 包含单次 API 调用的响应元数据。 通过 RequestOption 的 WithMeta 获取。

type Signer

type Signer struct {
	// contains filtered or unexported fields
}

Signer 执行阿里云 RPC API 的 HMAC-SHA1 签名计算。

func NewSigner

func NewSigner(cred Credential) *Signer

NewSigner 创建签名器。

func (*Signer) Sign

func (s *Signer) Sign(httpMethod string, params map[string]string) error

Sign 对所有查询参数生成阿里云 RPC 签名,并将签名相关字段写入 params。

入参 params 应包含除 Signature 自身外的所有查询参数 (Action、Format、Version、AccessKeyId、RegionId 及业务参数)。 Sign 会向 params 中写入 SignatureNonce、Timestamp、SignatureMethod、 SignatureVersion、Signature(及可选的 SecurityToken)。

签名算法:

StringToSign = HTTPMethod + "&" + percentEncode("/") + "&" + percentEncode(CanonicalizedQueryString)
Signature = Base64( HMAC-SHA1( StringToSign, AccessKeySecret + "&" ) )

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL