如何在 rust-warp 中发送自定义错误消息?

How to send a custom error message in rust-warp?

我使用 warp(0.3.2) 来处理几条路线(POST /topup 和 POST /print):

// main.rs
let print_route = warp::path("print")
    .and(warp::path::param::<String>())
    .and(warp::post())
    .and_then(handle_upload);

let topup_route = warp::path("topup")
    .and(warp::post())
    .and(warp::body::json())
    .and_then(handle_topup);

let routes = root
    .or(print_route)
    .or(topup_route)
    .recover(handle_rejection)
    .with(warp::cors().allow_any_origin());

println!("Client is up and running at localhost:{}", PORT);
warp::serve(routes).run(([0, 0, 0, 0], PORT)).await;

handle_uploadhandle_topup 函数上,我使用自定义错误类型 AppErr

use warp::{reject::Reject};

#[derive(Debug)]
pub struct AppErr {
    pub reason: String,
}

impl Reject for AppErr {}

如果我需要在 handle_topuphandle_upload 的任何部分抛出错误,我只需像这样调用 warp::reject::custom() 函数:

let reason = format!("Error writing file to destination: {}", &path);
warp::reject::custom(AppErr { reason })

我给出的每个 reason 都应该转发给客户端,所以我只做了一个 handle_rejection 来将两个路由中的任何 AppErr 转换为将被发送的响应类型扭曲

// Custom rejection handler that maps rejections into responses.
async fn handle_rejection(err: Rejection) -> Result<impl Reply, std::convert::Infallible> {
    if err.is_not_found() {
        Ok(reply::with_status("NOT_FOUND", StatusCode::NOT_FOUND))
    } else if let Some(e) = err.find::<AppErr>() {
        Ok(reply::with_status(e.reason.as_str(), StatusCode::BAD_REQUEST))
    } else {
        eprintln!("unhandled rejection: {:?}", err);
        Ok(reply::with_status(
            "INTERNAL_SERVER_ERROR",
            StatusCode::INTERNAL_SERVER_ERROR,
        ))
    }
}

应该很简单,但是我对这行有问题

error[E0597]: `err` does not live long enough
   --> src\main.rs:225:29
    |
225 |     } else if let Some(e) = err.find::<AppErr>() {
    |                             ^^^^^^^^^^^^^^^^^^^^
    |                             |
    |                             borrowed value does not live long enough
    |                             argument requires that `err` is borrowed for `'static`
...
234 | }
    | - `err` dropped here while still borrowed

如果它尝试克隆 e.reason,它会抛出 temporary value dropped while borrowed 错误。 我的问题是为什么我不能使 e 中的任何内容可用于 if let Some(e) = err.find::<AppErr>()

的外部范围

err 不能在函数作用域之后借用(因为它被它消耗了)。因此,您需要拥有所需零件的所有权:

async fn handle_rejection(err: Rejection) -> Result<impl Reply, std::convert::Infallible> {
    if err.is_not_found() {
        Ok(reply::with_status("NOT_FOUND".to_string(), StatusCode::NOT_FOUND))
    } else if let Some(e) = err.find::<AppErr>() {
        Ok(reply::with_status(e.reason.as_str().to_string(), StatusCode::BAD_REQUEST))
    } else {
        eprintln!("unhandled rejection: {:?}", err);
        Ok(reply::with_status(
            "INTERNAL_SERVER_ERROR".to_string(),
            StatusCode::INTERNAL_SERVER_ERROR,
        ))
    }
}

String 实现了 Reply(以及 &str),您可以在 documentation

中找到更多信息

您的 if/else 分支将有不同的类型(&strString),在所有分支中使用 to_string,或者您必须使用 Box<dyn Reply> 或使用 Cow.