如何使用 Rejection 和问号运算符处理 Warp 中的错误? [英] How do I handle errors in Warp using both Rejection and the question-mark operator?
问题描述
使用 warp.rs 0.2.2,让我们考虑一个具有 GET/
的一个路由的基本 Web 服务:
Using warp.rs 0.2.2, let's consider a basic web service with one route for 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(())
}
我的目标是在路由处理程序中使用 ?
进行错误处理,所以让我们在 crate::routes
中编写一个可以错误并尽早返回的程序:
My goal is to use ?
for error handling in the route handlers, so let's write one that can error and return early in 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 !")
}
这个版本有效.这里 Url::parse()
返回的错误是一个 url::ParseError
This version works.
Here the error that's returned by Url::parse()
is a url::ParseError
在错误类型之间转换,从url::ParseError
到ServiceError
,然后从ServiceError
到warp::Rejection
,我在 crate::errors
中编写了一些错误帮助器:
To convert between error types, from url::ParseError
to ServiceError
, then from ServiceError
to warp::Rejection
, I've written some error helpers in 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())
}
}
现在,上述工作,我正在尝试缩短第二个代码块以使用 ?
直接进行错误处理,并从底层错误自动转换(此处为 url::ParseError
) 到 warp::Rejection
.这是我尝试过的:
Now, the above works, and I'm trying to shorten the second code block to use ?
for error handling directly, and convert automatically from the underlying error (here url::ParseError
) to a warp::Rejection
.
Here's what I've tried:
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 !")
}
Url::Parse
返回的 url::ParseError
将很好地转换为 ServiceError 以返回,但从我的处理程序返回 ServiceError 不起作用.我得到的第一个编译错误是:
The url::ParseError
returned by Url::Parse
will convert fine into a ServiceError to return, but returning a ServiceError from my handler doesn't work.
The first compilation error I get is:
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`
有没有办法只使用 ?
来保持简短的错误处理:
Is there a way I can keep the short error handling using ?
only and either:
- 使
ServiceError
实现warp::reject::sealed::CombineRejection
? - 解决这个问题?
推荐答案
您可以实现 From
将您的错误类型转换为 warp::Rejection
使用 reject::custom
.Rejection
封装了自定义类型,您以后可以选择在 recover
处理程序.
You can implement From
to convert your error type into warp::Rejection
using reject::custom
. Rejection
encapsulates custom types which you can later choose to inspect inside of a recover
handler.
这个例子使用了一个简单的错误结构,但如果你有一个错误枚举,你可以匹配恢复处理程序中的变体并根据需要执行不同的逻辑.
This example uses a plain error struct, but if you have an error enum you can match on the variants inside the recovery handler and perform different logic as needed.
use serde::Deserialize;
use snafu::{ensure, Snafu};
use std::convert::Infallible;
use warp::{
filters::{any, query, BoxedFilter},
http::StatusCode,
reject::Reject,
Filter, Rejection, Reply,
};
// A normal error type, created by SNAFU
#[derive(Debug, Snafu)]
#[snafu(display("Expected a value less than 10, but it was {}", value))]
struct LessThanTenError {
value: i32,
}
// A function that might fail
fn validate(value: i32) -> Result<i32, LessThanTenError> {
ensure!(value < 10, LessThanTenContext { value });
Ok(value)
}
// We need a custom type to later extract from the `Rejection`. In
// this case, we can reuse the error type itself.
impl Reject for LessThanTenError {}
// To allow using `?`, we implement a conversion from our error to
// `Rejection`
impl From<LessThanTenError> for Rejection {
fn from(other: LessThanTenError) -> Self {
warp::reject::custom(other)
}
}
#[tokio::main]
async fn main() {
let api = simple_math().recover(report_invalid);
let p: std::net::SocketAddr = "0.0.0.0:8888".parse().unwrap();
warp::serve(api).run(p).await;
}
#[derive(Debug, Deserialize)]
struct QueryParams {
a: i32,
b: i32,
}
fn simple_math() -> BoxedFilter<(impl Reply,)> {
any::any()
.and(query::query())
.and_then(|args: QueryParams| async move {
// Look at us using those question marks!
let a = validate(args.a)?;
let b = validate(args.b)?;
let sum = validate(a + b)?;
// We specify that we are returning an error type of
// `Rejection`, which allows the compiler to know what
// type to convert to when using `?` here.
Ok::<_, Rejection>(format!("The sum is {}", sum))
})
.boxed()
}
async fn report_invalid(r: Rejection) -> Result<impl Reply, Infallible> {
if let Some(e) = r.find::<LessThanTenError>() {
// It was our specific error type, do whatever we want. We
// will just print out the error text.
Ok(warp::reply::with_status(
e.to_string(),
StatusCode::BAD_REQUEST,
))
} else {
// Do prettier error reporting for the default error here.
Ok(warp::reply::with_status(
String::from("Something bad happened"),
StatusCode::INTERNAL_SERVER_ERROR,
))
}
}
[dependencies]
serde = { version = "1.0.118", features = ["derive"] }
snafu = "0.6.10"
tokio = { version = "0.2.23", features = ["full"] }
warp = "0.2.5"
% curl 'http://127.0.0.1:8888'
< HTTP/1.1 500 Internal Server Error
Something bad happened
% curl -v 'http://127.0.0.1:8888?a=1&b=2'
< HTTP/1.1 200 OK
The sum is 3
% curl -v 'http://127.0.0.1:8888?a=6&b=5'
< HTTP/1.1 400 Bad Request
Expected a value less than 10, but it was 11
另见:
- 有没有办法在 Warp 中作为过滤器的一部分进行验证?
- 我应该在什么时候实施 std::convert::From 与 std::convert::Into?
- 如何在 Rust 中定义自定义的 `Error` 类型?
这篇关于如何使用 Rejection 和问号运算符处理 Warp 中的错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!