Какой самый идиоматичный способ обработки ошибок в асинхронных обработчиках в actix-web?
У меня есть асинхронный обработчик в actix_web, который должен завершиться ошибкой, если несколько заголовков не установлены. Я не понимаю, какой лучший способ обработки ошибок в функциях, которые возвращают 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()
},
);
Я решил отсутствие ?
оператор для фьючерсов путем сопоставления с ранним возвратом. Однако в моем коде мне нужно убедиться, что существует множество других заголовков.
В идеале я хотел бы извлечь логику сопоставления и раннего возврата к чему-то повторно используемому, но в этом случае это заставляет меня создавать макрос. Это кажется излишним, особенно если в языке уже есть что-то, что позволяет мне делать то, что я хочу.
Какой самый идиоматический способ справиться с этой ситуацией?
1 ответ
Чтобы обработать ошибки, верните ошибочный Future
, Например, проверьте заголовок как Future
а затем соедините ваше будущее с .and_then
, Хитрость заключается в том, чтобы сохранить типы ошибок фьючерсов одинаковыми, чтобы избежать 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::Error::from
который отображает все ошибки в один тип, failure::Error
,
Наконец, вы можете найти actix_web::guards
полезно для проверки значений заголовка:
.guard(guard::Header("x-client-id", "special client"))