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!("处理其他类型的错误");
        }
    }
}

主要功能

  1. 标准RPC错误代码:包含NEAR协议定义的所有标准RPC错误代码
  2. 自定义错误支持:允许开发者定义自己的错误类型并转换为RPC错误
  3. 错误元数据:支持附加错误详细信息作为JSON数据
  4. 错误序列化/反序列化:支持与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);
}

这个完整示例展示了:

  1. 定义更丰富的自定义错误类型
  2. 实现From trait进行错误转换
  3. 模拟NEAR节点调用场景
  4. 处理各种错误情况
  5. 错误序列化和反序列化
  6. 根据错误代码进行不同处理

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);
                }
            }
        }
    }
}

示例说明

  1. 自定义错误类型:定义了MyAppError枚举来表示应用程序特定的错误情况

  2. 错误转换:实现了From<MyAppError> for RpcError trait,将自定义错误转换为标准RPC错误

  3. 模拟RPC调用mock_rpc_call函数模拟了一个返回服务器错误的RPC调用

  4. 响应处理process_response函数展示了如何处理JSON响应并检查必要字段

  5. 错误包装:在make_rpc_request函数中,我们为原始错误添加了上下文信息

  6. 错误分类处理:在main函数中,根据不同的错误类型采取不同的处理策略

这个完整示例演示了如何在实际应用中使用near-rpc-error-core库进行全面的错误处理,包括自定义错误、错误转换、错误分类和上下文添加等最佳实践。

回到顶部