Rust NEAR协议RPC错误处理库near-rpc-error-core的使用,实现高效区块链节点通信错误管理与自定义错误类型
Rust NEAR协议RPC错误处理库near-rpc-error-core的使用,实现高效区块链节点通信错误管理与自定义错误类型
安装
在项目目录中运行以下Cargo命令:
cargo add near-rpc-error-core
或在Cargo.toml中添加以下内容:
near-rpc-error-core = "0.26.0"
使用示例
near-rpc-error-core
库提供了处理NEAR协议RPC错误的核心功能,包括标准错误类型和自定义错误支持。以下是完整的使用示例:
use near_rpc_error_core::{RpcError, RpcErrorCode};
use serde_json::json;
// 自定义错误类型
#[derive(Debug, Clone)]
enum MyCustomError {
ConnectionFailed,
InvalidResponse,
}
// 实现从自定义错误到RpcError的转换
impl From<MyCustomError> for RpcError {
fn from(err: MyCustomError) -> Self {
match err {
MyCustomError::ConnectionFailed => RpcError::new(
RpcErrorCode::InternalError,
"Connection to node failed".to_string(),
Some(json!({"retry_after": 30})),
),
MyCustomError::InvalidResponse => RpcError::new(
RpcErrorCode::InvalidParams,
"Received invalid response format".to_string(),
None,
),
}
}
}
fn main() {
// 示例1:创建标准RPC错误
let standard_error = RpcError::new(
RpcErrorCode::TimeoutError,
"Request timed out".to_string(),
Some(json!({"timeout_ms": 5000})),
);
println!("Standard Error: {:?}", standard_error);
// 示例2:使用自定义错误
let custom_error: RpcError = MyCustomError::ConnectionFailed.into();
println!("Custom Error: {:?}", custom_error);
// 示例3:解析RPC错误响应
let error_response = r#"{
"code": -32000,
"message": "Server error",
"data": {"details": "Internal server failure"}
}"#;
let parsed_error: RpcError = serde_json::from_str(error_response).unwrap();
println!("Parsed Error: {:?}", parsed_error);
// 示例4:处理特定错误代码
match parsed_error.code() {
RpcErrorCode::InternalError => {
println!("处理内部服务器错误");
},
RpcErrorCode::InvalidParams => {
println!("处理无效参数错误");
},
_ => {
println!("处理其他类型的错误");
}
}
}
主要功能
- 标准RPC错误代码:包含NEAR协议定义的所有标准RPC错误代码
- 自定义错误支持:允许开发者定义自己的错误类型并转换为RPC错误
- 错误元数据:支持附加错误详细信息作为JSON数据
- 错误序列化/反序列化:支持与JSON格式的相互转换
这个库特别适合需要与NEAR区块链节点进行RPC通信的应用程序,提供统一的错误处理机制和自定义错误扩展能力。
完整示例代码
以下是一个更完整的示例,展示了如何在实际应用中使用near-rpc-error-core库:
use near_rpc_error_core::{RpcError, RpcErrorCode};
use serde_json::json;
use thiserror::Error;
// 自定义错误类型
#[derive(Debug, Error)]
enum NodeClientError {
#[error("Connection to node failed: {0}")]
ConnectionFailed(String),
#[error("Invalid response format: {0}")]
InvalidResponseFormat(String),
#[error("Transaction failed: {0}")]
TransactionFailed(String),
#[error("Account not found: {0}")]
AccountNotFound(String),
}
// 实现从自定义错误到RpcError的转换
impl From<NodeClientError> for RpcError {
fn from(err: NodeClientError) -> Self {
match err {
NodeClientError::ConnectionFailed(msg) => RpcError::new(
RpcErrorCode::InternalError,
msg,
Some(json!({"category": "connection"})),
),
NodeClientError::InvalidResponseFormat(msg) => RpcError::new(
RpcErrorCode::ParseError,
msg,
Some(json!({"category": "response_parsing"})),
),
NodeClientError::TransactionFailed(msg) => RpcError::new(
RpcErrorCode::TransactionExecutionError,
msg,
Some(json!({"category": "transaction"})),
),
NodeClientError::AccountNotFound(msg) => RpcError::new(
RpcErrorCode::UnknownAccount,
msg,
Some(json!({"category": "account"})),
),
}
}
}
// 模拟调用NEAR节点的函数
fn call_near_node(endpoint: &str) -> Result<String, NodeClientError> {
if endpoint.is_empty() {
return Err(NodeClientError::ConnectionFailed("Empty endpoint".into()));
}
// 这里模拟各种错误情况
if endpoint.contains("invalid") {
return Err(NodeClientError::InvalidResponseFormat("Malformed data".into()));
}
if endpoint.contains("tx_fail") {
return Err(NodeClientError::TransactionFailed("Insufficient balance".into()));
}
if endpoint.contains("unknown") {
return Err(NodeClientError::AccountNotFound("Account alice.near not found".into()));
}
Ok("Success response".into())
}
fn main() {
// 测试各种错误场景
let test_cases = vec![
"",
"https://invalid.response",
"https://tx_fail.testnet",
"https://unknown.account",
"https://valid.node",
];
for endpoint in test_cases {
println!("\nTesting endpoint: {}", endpoint);
match call_near_node(endpoint) {
Ok(response) => println!("Success: {}", response),
Err(e) => {
// 将自定义错误转换为RpcError
let rpc_error: RpcError = e.into();
println!("RPC Error: {:?}", rpc_error);
// 根据错误代码处理不同情况
match rpc_error.code() {
RpcErrorCode::InternalError => {
println!("处理连接错误,建议重试");
},
RpcErrorCode::ParseError => {
println!("处理响应解析错误,检查API版本");
},
RpcErrorCode::TransactionExecutionError => {
println!("处理交易错误,检查账户余额");
},
RpcErrorCode::UnknownAccount => {
println!("处理未知账户错误,检查账户名");
},
_ => println!("处理其他错误"),
}
}
}
}
// 创建并序列化一个RPC错误
let sample_error = RpcError::new(
RpcErrorCode::TimeoutError,
"Request timed out".to_string(),
Some(json!({"timeout_ms": 5000})),
);
let serialized = serde_json::to_string(&sample_error).unwrap();
println!("\nSerialized Error: {}", serialized);
// 反序列化错误
let deserialized: RpcError = serde_json::from_str(&serialized).unwrap();
println!("Deserialized Error: {:?}", deserialized);
}
这个完整示例展示了:
- 定义更丰富的自定义错误类型
- 实现From trait进行错误转换
- 模拟NEAR节点调用场景
- 处理各种错误情况
- 错误序列化和反序列化
- 根据错误代码进行不同处理
1 回复
Rust NEAR协议RPC错误处理库near-rpc-error-core使用指南
完整示例Demo
下面是一个结合了基础用法和高级用法的完整示例,展示了如何在实际应用中使用near-rpc-error-core库:
use near_rpc_error_core::{RpcError, RpcErrorKind, ServerError};
use thiserror::Error;
use serde_json::{Error as JsonError, Value};
// 自定义错误类型
#[derive(Debug, Error)]
enum MyAppError {
#[error("Network timeout after {0} seconds")]
Timeout(u32),
#[error("Invalid JSON format")]
InvalidJson,
#[error("Missing required field: {0}")]
MissingField(String),
}
// 实现自定义错误到RpcError的转换
impl From<MyAppError> for RpcError {
fn from(err: MyAppError) -> Self {
match err {
MyAppError::Timeout(secs) => RpcError::new(
RpcErrorKind::ConnectionError,
format!("Request timed out after {} seconds", secs),
),
MyAppError::InvalidJson => RpcError::new(
RpcErrorKind::ParseError,
"Invalid JSON received".to_string(),
),
MyAppError::MissingField(field) => RpcError::new(
RpcErrorKind::InvalidParams,
format!("Required field '{}' is missing", field),
),
}
}
}
// 模拟RPC调用
fn mock_rpc_call() -> Result<String, RpcError> {
// 这里模拟一个服务器错误
let server_error = ServerError {
code: -32000,
message: "Internal server error".to_string(),
data: None,
};
Err(RpcError::ServerError(server_error))
}
// 处理RPC响应
fn process_response(response: &str) -> Result<Value, RpcError> {
// 解析JSON响应
let value: Value = serde_json::from_str(response)
.map_err(|e| RpcError::new(
RpcErrorKind::ParseError,
format!("Failed to parse JSON: {}", e)
))?;
// 检查必要字段
if !value.get("result").is_some() {
return Err(MyAppError::MissingField("result".to_string()).into());
}
Ok(value)
}
// 综合示例
fn make_rpc_request() -> Result<Value, RpcError> {
// 模拟RPC调用
let response = mock_rpc_call()
.map_err(|e| RpcError::new(
e.kind().clone(),
format!("RPC call failed: {}", e)
))?;
// 处理响应
let parsed = process_response(&response)?;
Ok(parsed)
}
fn main() {
match make_rpc_request() {
Ok(data) => println!("Success: {:?}", data),
Err(e) => {
// 错误分类处理
match e.kind() {
RpcErrorKind::ConnectionError => {
println!("网络连接错误,请检查网络后重试");
}
RpcErrorKind::ParseError => {
println!("数据解析错误,请联系技术支持");
}
RpcErrorKind::ServerError(server_err) => {
println!("服务器错误 (代码 {}): {}", server_err.code, server_err.message);
}
_ => {
println!("未知错误: {:?}", e);
}
}
}
}
}
示例说明
-
自定义错误类型:定义了
MyAppError
枚举来表示应用程序特定的错误情况 -
错误转换:实现了
From<MyAppError> for RpcError
trait,将自定义错误转换为标准RPC错误 -
模拟RPC调用:
mock_rpc_call
函数模拟了一个返回服务器错误的RPC调用 -
响应处理:
process_response
函数展示了如何处理JSON响应并检查必要字段 -
错误包装:在
make_rpc_request
函数中,我们为原始错误添加了上下文信息 -
错误分类处理:在main函数中,根据不同的错误类型采取不同的处理策略
这个完整示例演示了如何在实际应用中使用near-rpc-error-core库进行全面的错误处理,包括自定义错误、错误转换、错误分类和上下文添加等最佳实践。