Aptos Rust REST 客户端实战:aptos-rest-client 示例程序运行与源码解析
2026/9/17 2:37:57 网站建设 项目流程

Aptos Rust REST 客户端实战:aptos-rest-client 示例程序运行与源码解析

【免费下载链接】aptos-coreAptos is a layer 1 blockchain built to support the widespread use of blockchain through better technology and user experience.项目地址: https://gitcode.com/GitHub_Trending/ap/aptos-core

aptos-rest-client是 aptos-core 仓库中封装 Aptos Full Node REST API 的 Rust SDK 客户端。本仓库在 examples 目录 中提供了一组可执行示例(目前为account示例),官方文档将其定位为“端到端测试”:它们不接入标准cargo test流程,而是要求开发者本地(或远程)已有一个运行中的 Aptos API 服务,用真实请求验证客户端各条查询链路。读完本篇,你可以直接在本地跑通该示例,理解--api-url参数的解析方式、示例依次调用的每个 REST 端点,以及底层Client/ClientBuilder/State/RestError的关键实现细节。

一、示例的定位:面向真实 API 的端到端验证

crates/aptos-rest-client/examples/README.md 对示例的定位有两点关键说明:

  1. 这些示例实质上是REST 客户端的端到端测试("Really these examples serve as end-to-end tests for the REST client");
  2. 它们不是标准测试,不应该、也不会随标准cargo test一起运行,因为执行它们的前提是:已经有一个 Aptos API 在运行

从 示例入口 可以看到参数定义:

#[derive(Debug, Parser)] #[clap(author, version, about)] pub struct Args { /// This should include the port, e.g. http://127.0.0.1:8080 #[clap(long)] api_url: Url, }

api_url是一个url::Url类型字段,注释明确要求必须带上端口。这意味着 URL 必须是合法的绝对地址(如http://127.0.0.1:8080),传入不合法字符串时 clap 会在解析阶段直接报错退出,而不是运行到一半才发现地址错误。

二、运行方式:一条命令打满一条查询链路

aptos-rest-client包的父目录(即 crates/aptos-rest-client)执行:

cargo run --example <dir>

文档给出的完整示例是:

cargo run --example account -- --api-url http://127.0.0.1:8080

其中:

  • account对应examples/account/main.rs,是目录名即 example 名;
  • --api-url后面必须是指向 Full Node REST 服务的地址,默认本地 Full Node 的 REST 端口为8080

运行成功后,示例会以info!日志逐条打印查询结果,形如:

Running all queries against account: 0x0000000000000000000000000000000000000000000000000000000000000001 Successfully retrieved N account resources with JSON Successfully retrieved N account resources with BCS Successfully retrieved N account modules with JSON Successfully retrieved N account modules with BCS Successfully retrieved resource 0x1::chain_id::ChainId with JSON Successfully retrieved balance ... Successfully retrieved resource 0x1::chain_id::ChainId with BCS

示例入口在 main.rs L22-L31 中初始化日志、解析参数并构造客户端:

let args = Args::parse(); let client = Client::new(args.api_url); let address = AccountAddress::ONE; info!("Running all queries against account: {}", address);

所有查询统一打在系统账户0x1AccountAddress::ONE,即框架核心代码地址)上,该账户天然持有ChainId资源,保证查询不会落空。

三、account 示例逐条调用链拆解

按 main.rs L33-L100 的执行顺序,示例覆盖了两类端点形态:JSON 与 BCS。

3.1 账户资源与模块的整页查询(JSON + BCS 双形态)

let results = client.get_account_resources(address).await?; // JSON let results = client.get_account_resources_bcs(address).await?; // BCS let results = client.get_account_modules(address).await?; // JSON let results = client.get_account_modules_bcs(address).await?; // BCS

这四个方法对应GET /v1/accounts/{address}/resourcesGET /v1/accounts/{address}/modules端点。注意它们的实现并非单次请求,而是自动游标分页:在 src/lib.rs 中,get_account_resources内部调用paginate_with_cursor,每页请求量由常量控制:

const RESOURCES_PER_CALL_PAGINATION: u64 = 9999; const MODULES_PER_CALL_PAGINATION: u64 = 1000;

(见 lib.rs L64-L65。)分页循环会持续携带服务端返回的cursor请求下一页,直到响应头中不再带游标为止(paginate_with_cursor 实现)。因此results.inner().len()打印的是跨页合并后的总数,这也是示例日志中数字可能远超单页上限的原因。

3.2 单个资源查询:0x1::chain_id::ChainId

let resource = "0x1::chain_id::ChainId"; client.get_account_resource(address, resource).await?; // JSON,返回 Option<Resource> client.get_account_resource_bcs::<ChainId>(address, resource).await?; // BCS,直接反序列化为 ChainId 类型

这里体现了一条重要设计:BCS 泛型方法get_account_resource_bcs<T: DeserializeOwned>直接把 BCS 字节解码为目标 Rust 类型(lib.rs L1212-L1224),而 JSON 版本返回的是Option<Resource>,由调用方决定是否解析data字段。示例同时验证两条路径,确保两种序列化格式都能正确返回ChainId资源。

3.3 新旧两套余额查询接口的交叉校验

示例中最有“端到端测试”味道的一段是 main.rs L77-L94:

let balance_from_new_api = client .get_account_balance(address, "0x1::aptos_coin::AptosCoin") .await?; let balance_from_old_api = client.view_apt_account_balance(address).await?; assert_eq!(balance_from_new_api.inner(), balance_from_old_api.inner());
  • get_account_balance走的是GET /v1/accounts/{address}/balance/{asset_type}端点,asset_type可以是 coin 类型或 fungible asset 元数据地址(见 lib.rs L318-L335 的方法注释);
  • view_apt_account_balance走的是 view function 端点POST /v1/view,内部执行0x1::coin::balance(view_account_balance_bcs_impl)。

两条路径语义等价,示例用assert_eq!断言二者结果一致——任何一侧端点行为回归都会直接让程序 panic 失败。

3.4 一个容易被忽略的细节:示例自带单元测试

main.rs L105-L109 在main之外还定义了一个真正的单元测试:

#[test] fn verify_tool() { use clap::CommandFactory; Args::command().debug_assert() }

它用 clap 的debug_assert校验参数定义(如--api-url长选项与字段名一致、帮助文本无冲突等)。也就是说,虽然示例本身不在标准测试里跑,但这条 clap 参数自检仍然会随cargo test -p aptos-rest-client一起执行,保证命令行接口不会在重构中悄悄损坏。

四、客户端底层实现要点(源码佐证)

示例里一行Client::new(args.api_url)背后,ClientBuilder做了不少默认配置,理解它们对排查真实环境问题很有帮助。

4.1 默认配置:版本路径、超时、请求头

client_builder.rs L34-L109 中:

  • 版本路径前缀:默认为v1/DEFAULT_VERSION_PATH_BASE,见 lib.rs L58)。构造 URL 时所有端点路径都会先拼接该前缀,例如accounts/0x1/resources最终变成{base}/v1/accounts/0x1/resources。若自定义base_url已带路径(如https://host/v2),get_version_path_with_base会优先沿用 base URL 自带的路径(lib.rs L1927-L1941);
  • 超时:默认 10 秒(timeout: Duration::from_secs(10),client_builder.rs L54),可通过ClientBuilder::timeout()覆盖;
  • 客户端标识头:自动注入X-Aptos-Client请求头,取值为aptos-rust-sdk/{crate 版本号}(client_builder.rs L44-L48),服务端可据此统计 SDK 流量;
  • API Key 环境变量:若环境中设置了X_API_KEY,会自动以Authorization: Bearer {key}形式附加到请求头(client_builder.rs L58-L60)。对接需要密钥的托管 API 网关时无需改代码;
  • Cookie 存储开启(cookie_store(true)),以适配需要会话态的 API 网关场景。

4.2 每次响应的 State 元数据:从 HTTP 头解析

所有成功的响应都会被包装成Response<T>,其中附带一个State结构(state.rs L10-L21),包含chain_idepochledger versiontimestampblock_height、分页cursorencryption_key等字段。这些值全部来自服务端响应头中的X-Aptos-Chain-IdX-Aptos-Ledger-VersionX-Aptos-Cursor等自定义头(State::from_headers)。

这一点有两层实际意义:

  1. 一致性校验get_ledger_information内部会断言头解析出的 chain_id/epoch/version 与响应体字段一致(lib.rs L397-L417);
  2. 分页闭环paginate_with_cursor依赖的游标就是从X-Aptos-Cursor头里取的(cursor.clone_from(&response.state().cursor))。如果服务端少返回这些头,State::from_headers会直接报错而非静默继续——这是排查“分页卡死/数据缺失”时应先检查的点。

4.3 错误模型与可重试判断

客户端错误统一收敛到RestError枚举(error.rs L146-L162),区分 API 错误(Api,带状态码与链上状态)、BCS/JSON 反序列化错误、URL 解析错误、HTTP 层错误等。对于需要重试的场景,lib.rs L1943-L1957 提供retriable判定:仅429 Too Many Requests500502503504507 Insufficient Storage视为可重试,另有retriable_with_404变体把 404 也纳入可重试范围。配套的try_until_ok辅助函数(lib.rs L1784-L1834)以指数退避(初始 1s,翻倍,默认总等待 60s)执行重试。示例程序未直接使用重试逻辑,但生产代码里对全节点 API 的轮询(如wait_for_transaction_by_hash)本质上依赖同样的状态机思路:轮询间隔 500ms,并带“服务端滞后容忍窗口”(默认 60s,DEFAULT_MAX_SERVER_LAG_WAIT_DURATION)以避免因 Full Node 同步延迟而误判交易丢失。

五、适用前提与注意事项

  • 必须先有运行中的 Aptos API:本地 Full Node 默认 REST 端口为8080,也可指向 Devnet/Testnet 的公共 API 地址。ClientBuilder同时内置了AptosBaseUrl::Mainnet/Devnet/Testnet三个预设地址(client_builder.rs L16-L32),示例使用的是Custom分支,即你自己传入的 URL;
  • 示例不是单元测试:不要在 CI 里把cargo run --example account当作无依赖测试运行,它需要外部服务,失败与否取决于 API 端状态;
  • 依赖版本aptos-rest-client依赖aptos-api-typesaptos-typesreqwest等 workspace 依赖(见 Cargo.toml L19-L34),作为库引入其他工程时注意这些传递依赖;
  • 日志输出:示例通过aptos_logger::Logger::new().init()初始化日志,debug!/info!输出会按日志级别落到终端,排查连接问题时可调高日志级别观察重试与轮询细节。

六、延伸阅读路径

内容路径
示例运行说明(本文主体文档)crates/aptos-rest-client/examples/README.md
account 示例入口crates/aptos-rest-client/examples/account/main.rs
客户端核心实现(Client、分页、重试)crates/aptos-rest-client/src/lib.rs
构建器与默认配置crates/aptos-rest-client/src/client_builder.rs
State 头解析crates/aptos-rest-client/src/state.rs
错误类型crates/aptos-rest-client/src/error.rs
包定义与依赖crates/aptos-rest-client/Cargo.toml

综上,examples/account虽然只有百余行代码,但恰好串起了aptos-rest-client最常用的能力面:双序列化格式(JSON/BCS)的资源与模块查询、单资源精确读取、新旧余额接口的等价性校验,以及 clap 参数自检。把它当作“可运行的接口契约”来跑一遍,再对照上文的ClientBuilder默认值、State头解析与RestError模型阅读源码,是理解这个 Rust SDK 最快的路径。

【免费下载链接】aptos-coreAptos is a layer 1 blockchain built to support the widespread use of blockchain through better technology and user experience.项目地址: https://gitcode.com/GitHub_Trending/ap/aptos-core

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询