错误处理

通过主动解读错误并做出响应,提供更一致的用户体验。无论您是在开发自动云工作流还是与远程 API 交互,Rust 客户端库都提供了妥善处理错误的方法。本指南介绍了如何:

  • 处理错误: 检查错误类型,并根据服务状态代码(例如在遇到 NotFound 错误时创建缺失的资源)分支应用逻辑。
  • 检查错误详情: 提取并检查服务 Google Cloud 返回的丰富错误详情(例如错误 请求字段违规或配额失败),以排查 API 问题并动态调整运行时行为。
  • 解决绑定错误: 解读并解决因请求字段无效或缺失而导致的客户端 HTTP 绑定错误,确保您的请求顺利到达服务。

前提条件

本指南使用 Secret Manager 服务Cloud Natural Language API来演示错误处理。如需运行示例,请先执行以下操作:

  1. 启用 Secret Manager 服务
  2. 启用 Cloud Natural Language API
  3. 设置身份验证

依赖项

使用以下命令将所需的依赖项添加到 Cargo.toml 文件:

cargo add google-cloud-secretmanager-v1 google-cloud-gax crc32c google-cloud-language-v2

处理错误

Rust 客户端库可让您发现错误并做出响应。例如,您可以使用错误发现来分支行为:云服务中的常见模式是使用资源,就好像其容器存在一样,仅在遇到错误时才创建容器。如果容器通常存在,则此方法比在发出请求之前检查容器是否存在更高效。

以下示例演示了如何在尝试更新 Secret Manager 密文时捕获错误,并在密文尚不存在时创建它,从而处理缺失的资源。

  1. 尝试创建新的密文版本:

    match update_attempt(&client, project_id, secret_id, data.clone()).await {

  2. 如果 update_attempt 成功,请输出成功结果并返回:

    Ok(version) => {
        println!("new version is {}", version.name);
        Ok(version)
    }

  3. 如果 update_attempt 失败,您必须消除失败原因的歧义。 请求可能会因多种原因而失败,例如连接断开或身份验证令牌出错。重试政策可以处理大多数此类错误。查找服务返回的错误:

    Err(e) => {
        if let Some(status) = e.downcast_ref::<Error>().and_then(|e| e.status()) {

  4. 查找与缺失的密文对应的错误:

    if status.code == Code::NotFound {

  5. 如果您遇到“未找到”错误 (Code::NotFound),请尝试创建密文:

    let _ = create_secret(&client, project_id, secret_id).await?;

  6. 尝试再次添加密文版本。这次,如果任何操作失败,则返回错误:

    let version = update_attempt(&client, project_id, secret_id, data).await?;
    println!("new version is {}", version.name);
    return Ok(version);

代码示例:main 函数 (sample)

此示例的完整代码分为三个部分:主编排函数 (sample),后跟其两个辅助方法 (update_attemptcreate_secret)。

sample 函数尝试向密文添加新版本。它会捕获客户端返回的错误,并检查该错误是否为 Code::NotFound 错误。如果未找到密文,该函数会创建最初缺失的密文并重试更新。

use google_cloud_gax::error::Error;
use google_cloud_gax::error::rpc::Code;
use google_cloud_secretmanager_v1::client::SecretManagerService;
use google_cloud_secretmanager_v1::model::SecretVersion;

pub async fn sample(
    project_id: &str,
    secret_id: &str,
    data: Vec<u8>,
) -> anyhow::Result<SecretVersion> {
    let client = SecretManagerService::builder().build().await?;

    match update_attempt(&client, project_id, secret_id, data.clone()).await {
        Ok(version) => {
            println!("new version is {}", version.name);
            Ok(version)
        }
        Err(e) => {
            if let Some(status) = e.downcast_ref::<Error>().and_then(|e| e.status()) {
                if status.code == Code::NotFound {
                    let _ = create_secret(&client, project_id, secret_id).await?;
                    let version = update_attempt(&client, project_id, secret_id, data).await?;
                    println!("new version is {}", version.name);
                    return Ok(version);
                }
            }
            Err(e)
        }
    }
}

代码示例:辅助方法 (update_attempt)

辅助方法 update_attempt 尝试添加密文版本,计算载荷数据的 CRC32c 校验和:

use google_cloud_secretmanager_v1::client::SecretManagerService;
use google_cloud_secretmanager_v1::model::{SecretPayload, SecretVersion};

pub(crate) async fn update_attempt(
    client: &SecretManagerService,
    project_id: &str,
    secret_id: &str,
    data: Vec<u8>,
) -> anyhow::Result<SecretVersion> {
    let checksum = crc32c::crc32c(&data) as i64;
    let version = client
        .add_secret_version()
        .set_parent(format!("projects/{project_id}/secrets/{secret_id}"))
        .set_payload(
            SecretPayload::new()
                .set_data(data)
                .set_data_crc32c(checksum),
        )
        .send()
        .await?;
    Ok(version)
}

代码示例:辅助方法 (create_secret)

辅助方法 create_secret 会创建缺失的密文并配置自定义重试政策:

use google_cloud_gax::options::RequestOptionsBuilder;
use google_cloud_gax::retry_policy::AlwaysRetry;
use google_cloud_gax::retry_policy::RetryPolicyExt;
use google_cloud_secretmanager_v1::client::SecretManagerService;
use google_cloud_secretmanager_v1::model::{Replication, Secret, replication};
use std::time::Duration;

pub async fn create_secret(
    client: &SecretManagerService,
    project_id: &str,
    secret_id: &str,
) -> anyhow::Result<Secret> {
    let secret = client
        .create_secret()
        .set_parent(format!("projects/{project_id}"))
        .with_retry_policy(
            AlwaysRetry
                .with_attempt_limit(5)
                .with_time_limit(Duration::from_secs(60)),
        )
        .set_secret_id(secret_id)
        .set_secret(
            Secret::new()
                .set_replication(Replication::new().set_replication(
                    replication::Replication::Automatic