actix-web 中异步处理程序中处理错误的最惯用方法是什么?
What is the most idiomatic way of handling errors in asynchronous handlers in actix-web?
我在 actix_web 中有一个异步处理程序,如果未设置多个 headers,它一定会失败。我不明白处理 return Future
函数错误的最佳方法应该是什么。我基本上想要一个相当于 ?
操作符的期货。
这是我当前的代码:
r.post().with_async(
move |req: HttpRequest, path: Path<EventPath>, body: Json<EventCreationRequest>| {
let headers = req.headers();
let client_id = match headers
.get("x-client-id")
.ok_or("Header not found")
.and_then(|v| v.to_str().map_err(|_| "Invalid header content"))
{
Err(e) => return ok(HttpResponse::BadRequest().body(e)).responder(),
Ok(v) => v.to_string(),
};
operation_that_returns_future()
.map(|_| HttpResponse::Ok().body("OK!"))
.responder()
},
);
我通过匹配一个早期的 return 解决了期货缺少 ?
运算符的问题。但是,在我的代码中,我实际上需要确保存在一堆其他 headers。
理想情况下,我想将匹配和早期 return 逻辑提取到可重用的东西中,但在这种情况下,这迫使我创建一个宏。这似乎有点矫枉过正,特别是如果语言中已经有一些东西可以让我做我想做的事。
处理这种情况最惯用的方法是什么?
要处理错误,return 失败 Future
。例如,将 header 检查为 Future
,然后用 .and_then
链接你的期货。一个技巧是保持 futures 的错误类型相同,以避免 map_err
。例如:
fn handler(req: HttpRequest) -> impl Future<Item = HttpResponse, Error = Error> {
has_client_header(&req)
.and_then(|client| operation_that_returns_future(client))
.map(|result| HttpResponse::Ok().body(result))
}
fn has_client_header(req: &HttpRequest) -> impl Future<Item = String, Error = Error> {
if let Some(Ok(client)) = req.headers().get("x-client-id").map(|h| h.to_str()) {
future::ok(client.to_owned())
} else {
future::failed(ErrorBadRequest("invalid x-client-id header"))
}
}
fn operation_that_returns_future(client: String) -> impl Future<Item = String, Error = Error> {
future::ok(client)
}
结果:
$ curl localhost:8000
invalid x-client-id header⏎
$ curl localhost:8000 -H 'x-client-id: asdf'
asdf⏎
当operation_that_returns_future
有另一种错误类型时:
fn handler(req: HttpRequest) -> impl Future<Item = HttpResponse, Error = Error> {
has_client_header(&req)
.and_then(|client| {
operation_that_returns_future(client)
.map_err(|_| ErrorInternalServerError("operation failed"))
})
.map(|result| HttpResponse::Ok().body(result))
}
另一个技巧是使用 failure crate,它提供 failure::Error::from
将所有错误映射到一种类型,failure::Error
。
最后,您可能会发现 actix_web::guards
对于检查 header 值很有用:
.guard(guard::Header("x-client-id", "special client"))
我在 actix_web 中有一个异步处理程序,如果未设置多个 headers,它一定会失败。我不明白处理 return Future
函数错误的最佳方法应该是什么。我基本上想要一个相当于 ?
操作符的期货。
这是我当前的代码:
r.post().with_async(
move |req: HttpRequest, path: Path<EventPath>, body: Json<EventCreationRequest>| {
let headers = req.headers();
let client_id = match headers
.get("x-client-id")
.ok_or("Header not found")
.and_then(|v| v.to_str().map_err(|_| "Invalid header content"))
{
Err(e) => return ok(HttpResponse::BadRequest().body(e)).responder(),
Ok(v) => v.to_string(),
};
operation_that_returns_future()
.map(|_| HttpResponse::Ok().body("OK!"))
.responder()
},
);
我通过匹配一个早期的 return 解决了期货缺少 ?
运算符的问题。但是,在我的代码中,我实际上需要确保存在一堆其他 headers。
理想情况下,我想将匹配和早期 return 逻辑提取到可重用的东西中,但在这种情况下,这迫使我创建一个宏。这似乎有点矫枉过正,特别是如果语言中已经有一些东西可以让我做我想做的事。
处理这种情况最惯用的方法是什么?
要处理错误,return 失败 Future
。例如,将 header 检查为 Future
,然后用 .and_then
链接你的期货。一个技巧是保持 futures 的错误类型相同,以避免 map_err
。例如:
fn handler(req: HttpRequest) -> impl Future<Item = HttpResponse, Error = Error> {
has_client_header(&req)
.and_then(|client| operation_that_returns_future(client))
.map(|result| HttpResponse::Ok().body(result))
}
fn has_client_header(req: &HttpRequest) -> impl Future<Item = String, Error = Error> {
if let Some(Ok(client)) = req.headers().get("x-client-id").map(|h| h.to_str()) {
future::ok(client.to_owned())
} else {
future::failed(ErrorBadRequest("invalid x-client-id header"))
}
}
fn operation_that_returns_future(client: String) -> impl Future<Item = String, Error = Error> {
future::ok(client)
}
结果:
$ curl localhost:8000
invalid x-client-id header⏎
$ curl localhost:8000 -H 'x-client-id: asdf'
asdf⏎
当operation_that_returns_future
有另一种错误类型时:
fn handler(req: HttpRequest) -> impl Future<Item = HttpResponse, Error = Error> {
has_client_header(&req)
.and_then(|client| {
operation_that_returns_future(client)
.map_err(|_| ErrorInternalServerError("operation failed"))
})
.map(|result| HttpResponse::Ok().body(result))
}
另一个技巧是使用 failure crate,它提供 failure::Error::from
将所有错误映射到一种类型,failure::Error
。
最后,您可能会发现 actix_web::guards
对于检查 header 值很有用:
.guard(guard::Header("x-client-id", "special client"))