如何使用 Rejection 和问号运算符处理 Warp 中的错误?

Nic*_*all 6 error-handling http rust rust-warp

使用warp.rs 0.2.2,让我们考虑一个基本的 Web 服务,它有一个路由GET /

#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
    let getRoot = warp::get().and(warp::path::end()).and_then(routes::getRoot);
    warp::serve(getRoot).run(([0, 0, 0, 0], 3030)).await;
    Ok(())
}
Run Code Online (Sandbox Code Playgroud)

我的目标是?在路由处理程序中用于错误处理,所以让我们编写一个可以错误并在早期返回的程序crate::routes

use crate::errors::ServiceError;
use url::Url;

pub async fn getRoot() -> Result<impl warp::Reply, warp::Rejection> {
    let _parsed_url = Url::parse(&"https://whydoesn.it/work?").map_err(ServiceError::from)?;

    Ok("Hello world !")
}
Run Code Online (Sandbox Code Playgroud)

这个版本有效。这里返回的错误Url::parse()url::ParseError

为了在错误类型之间转换,从url::ParseErrorServiceError,然后从ServiceErrorwarp::Rejection,我写了一些错误助手crate::errors

#[derive(thiserror::Error, Debug)]
pub enum ServiceError {
    #[error(transparent)]
    Other(#[from] anyhow::Error), // source and Display delegate to anyhow::Error
}
impl warp::reject::Reject for ServiceError {}
impl From<ServiceError> for warp::reject::Rejection {
    fn from(e: ServiceError) -> Self {
        warp::reject::custom(e)
    }
}
impl From<url::ParseError> for ServiceError {
    fn from(e: url::ParseError) -> Self {
        ServiceError::Other(e.into())
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,上述工作,我正在尝试缩短第二个代码块以?直接用于错误处理,并自动从底层错误(此处url::ParseError)转换为warp::Rejection. 这是我尝试过的:

use crate::errors::ServiceError;
use url::Url;

pub async fn getRoot() -> Result<impl warp::Reply, ServiceError> {
    let _parsed_url = Url::parse(&"https://whydoesn.it/work?")?;

    Ok("Hello world !")
}
Run Code Online (Sandbox Code Playgroud)

url::ParseError通过返回Url::Parse将其转换成精细一个服务错误,以回报,但是从我的处理程序返回一个服务错误不起作用。我得到的第一个编译错误是:

error[E0277]: the trait bound `errors::ServiceError: warp::reject::sealed::CombineRejection<warp::reject::Rejection>` is not satisfied
   --> src/main.rs:102:54
    |
102 |     let getRoot = warp::get().and(warp::path::end()).and_then(routes::getRoot);
    |                                                      ^^^^^^^^ the trait `warp::reject::sealed::CombineRejection<warp::reject::Rejection>` is not implemented for `errors::ServiceError`
Run Code Online (Sandbox Code Playgroud)

有没有一种方法可以让我?只使用以下任一方法来保持简短的错误处理:

  • 使ServiceError实施warp::reject::sealed::CombineRejection<warp::reject::Rejection>?
  • 解决这个问题?

log*_*ina 1

根据我的发现,有两种解决方案。

  1. 放弃?并转而使用您自己的宏,该宏会在出现错误时构造并返回响应。

  2. 使用cjbassi 的PR #458代替主线版本:

    • 实现warp::reply::Reply您的错误类型,以便将其转换为正确的用户面临的错误消息。
    • 在 Cargo.toml 文件中替换warp = "0.2"warp = { git = "https://github.com/cjbassi/warp.git", branch = "error"}
    • 使用.map_async而不是.and_then处理程序