Rust中API请求与错误处理实践
立即解锁
发布时间: 2025-09-04 01:54:29 阅读量: 6 订阅数: 53 AIGC 

# Rust 中 API 请求与错误处理实践
## 1. 引言
在开发 Rust 应用程序时,与外部 API 进行交互是常见的需求。在这个过程中,我们需要处理各种可能出现的情况,包括成功响应、错误响应以及超时等问题。本文将详细介绍如何在 Rust 中处理 API 请求和错误,同时展示如何优化代码以提高可维护性和可扩展性。
## 2. API 响应类型定义
### 2.1 基本类型定义
为了处理 API 的响应,我们需要定义相应的数据结构。以下是一些示例结构体:
```rust
#[derive(Deserialize, Serialize, Debug, Clone)]
struct BadWord {
original: String,
word: String,
deviations: i64,
info: i64,
#[serde(rename = "replacedLen")]
replaced_len: i64,
}
#[derive(Deserialize, Serialize, Debug, Clone)]
struct BadWordsResponse {
content: String,
bad_words_total: i64,
bad_words_list: Vec<BadWord>,
censored_content: String,
}
```
这些结构体用于表示 API 的成功响应。
### 2.2 错误处理
除了成功响应,我们还需要处理 API 返回的错误。通过 `reqwest` 库的 `status()` 方法,我们可以检查响应的状态码,并判断是客户端错误(4xx)还是服务器错误(5xx)。以下是一个更新后的路由处理函数示例:
```rust
pub async fn add_question(
store: Store,
new_question: NewQuestion,
) -> Result<impl warp::Reply, warp::Rejection> {
let client = reqwest::Client::new();
let res = client
.post("https://api.apilayer.com/bad_words?censor_character={censor_character}'")
.header("apikey", "API_KEY")
.body(new_question.content)
.send()
.await
.map_err(|e| handle_errors::Error::ExternalAPIError(e))?;
if !res.status().is_success() {
let status = res.status().as_u16();
let message = res.json::<APIResponse>().await.unwrap();
let err = handle_errors::APILayerError {
status,
message: message.0,
};
if status < 500 {
return Err(warp::reject::custom(handle_errors::Error::ClientError(err)));
} else {
return Err(warp::reject::custom(handle_errors::Error::ServerError(err)));
}
}
let res = res.json::<BadWordsResponse>()
.await
.map_err(|e| handle_errors::Error::ExternalAPIError(e))?;
let content = res.censored_content;
let question = NewQuestion {
title: new_question.title,
content,
tags: new_question.tags,
};
match store.add_question(question).await {
Ok(_) => Ok(warp::reply::with_status("Question added", StatusCode::OK)),
Err(e) => Err(warp::reject::custom(e)),
}
}
```
在这个函数中,我们首先发送请求,然后检查响应状态。如果状态码不是成功状态,我们会根据状态码创建相应的错误类型并返回。
### 2.3 错误类型扩展
为了更好地处理不同类型的错误,我们扩展了 `handle-errors` crate 中的 `Error` 枚举:
```rust
#[derive(Debug)]
pub enum Error {
ParseError(std::num::ParseIntError),
MissingParameters,
DatabaseQueryError,
ExternalAPIError(ReqwestError),
ClientError(APILayerError),
ServerError(APILayerError)
}
#[derive(Debug, Clone)]
pub struct APILayerError {
pub status: u16,
pub message: String,
}
impl std::fmt::Display for APILayerError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Status: {}, Message: {}", self.status, self.message)
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match &*self {
Error::ParseError(ref err) => write!(f, "Cannot parse parameter: {}", err),
Error::MissingParameters => write!(f, "Missing parameter"),
Error::DatabaseQueryError => write!(f, "Cannot update, invalid data."),
Error::ExternalAPIError(err) => write!(f, "External API error: {}", err),
Error::ClientError(err) => write!(f, "External Client error: {}", err),
Error::ServerError(err) => write!(f, "External Server error: {}", err),
}
}
}
impl Reject for Error {}
impl Reject for APILayerError {}
```
这样,我们可以更清晰地区分不同类型的错误,并进行相应的处理。
## 3. 代码复用与重构
### 3.1 提取公共代码
为了避免在每个路由处理函数中重复编写 API 请求和错误处理的代码,我们将这些代码提取到一个单独的文件 `profanity.rs` 中:
```rust
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct APIResponse(String);
#[derive(Deserialize, Serialize, Debug, Clone)]
struct BadWord {
original: String,
word: String,
deviations: i64,
info: i64,
#[serde(rename = "replacedLen")]
replaced_len: i64,
}
#[derive(Deserialize, Serialize, Debug, Clone)]
struct BadWordsResponse {
content: String,
bad_words_total: i64,
bad_words_list: Vec<BadWord>,
censored_content: String,
}
pub async fn check_profanity(content: String) -> Result<String, handle_errors::Error> {
let client = reqwest::Client::new();
let res = client
.post("https://api.apilayer.com/bad_words?censor_character={censor_character}'")
.header("apikey", "API_KEY")
.body(content)
.send()
.await
.map_err(|e| handle_errors::Error::Extern
```
0
0
复制全文
相关推荐










