Warp tiene cosas horribles chavales. En BitTorrent al toque #23 ya comente el tema de devolver las respuestas http que tienen un par de helpers que parece que los ha escrito un niño pequeño.
Traigo otro mojon de mierda. Para que veáis que por mucho Rust y libreria super popular que sea, no significa que la gente que escriba estas librerías tiene idea de lo que hace. La mayoria de gente estaba en el momento y lugar indicado para aprovecharse del tirón... y poco mas.
https://github.com/seanmonstar/warp/issues/388#issuecomment-576453485
Quiero gestionar errores de manera eficiente en mis endpoints. Como os he comentado en caso de que algo pete para liberar las keys de nuestro lock. Pues bien, la manera de hacerlo es o creando filtros de mappear errores o tener un recover genérico. Que esto ultimo es la recomendación oficial...
Tengo:
let put_record = warp::put()
.and(lock_keys.clone())
.and(warp::header::optional::<u64>("content-length"))
.and(warp::path::param::<String>())
.and(warp::body::bytes())
.and(warp::path::end())
.and_then(handle_put_record);
let get_record = warp::get()
.and(warp::path::param::<String>())
.and(warp::path::end())
.and_then(handle_get_record);
let api = put_record.or(get_record).recover(handle_rejection);
No podemos tener:
let put_record = warp::put()
.and(lock_keys.clone())
.and(warp::header::optional::<u64>("content-length"))
.and(warp::path::param::<String>())
.and(warp::body::bytes())
.and(warp::path::end())
.and_then(handle_put_record).recover(put_recover); // RECOVER
let get_record = warp::get()
.and(warp::path::param::<String>())
.and(warp::path::end())
.and_then(handle_get_record).recover(get_recover); // RECOVER
let api = put_record.or(get_record);
Menudo coñazo... https://github.com/seanmonstar/warp/blob/master/examples/rejections.rs
Imagino que con el sistema de tirado que ofrece la API lo puedo tener como un filtro que hace wrap de otro filtro: https://github.com/seanmonstar/warp/blob/master/examples/wrapping.rs
Pero vamos. La conclusion es que esto es una puta porquería de API. Se nota que lo han escrito niños de DAW que no han trabajado en su vida. Como el 99% del código open source que por desgracia toca usar si no quieres re-escribir hasta las notas del apuntador.
Con lo fácil que es tener un map_err
: - ) pero no, me tocara si quiero tener un map que haga match de si es Ok o Err yo a mano y si es Err arreglarlo. Y si es un panic cogerlo en el recover... Y tener la lógica de gestión de errores repartida por todos lados. Genial fperos. Como la gente puede ser tan mala escribiendo código. Cuando la propia std lib de rust tiene map, map_err, ok_or_else, y mil historias del estilo para estos casos de uso! Que pereza.
Fijaros en la API, en lugar de tener helpers para los métodos solo tienen uno para el not_found, sino tienes que ir uno a uno comprovando:
if err.is_not_found() {
code = StatusCode::NOT_FOUND;
message = "NOT_FOUND";
} else if let Some(DivideByZero) = err.find() {
code = StatusCode::BAD_REQUEST;
message = "DIVIDE_BY_ZERO";
} else if let Some(e) = err.find::<warp::filters::body::BodyDeserializeError>() {
} else if let Some(_) = err.find::<warp::reject::MethodNotAllowed>() {
} else {
}
vs algo que podria ser asi:
match err {
rejection::NotFound => {}
rejection::BodyDeserializeError => {}
rejection::MethodNotAllowed => {}
_ =>
}
Que automáticamente te pusiese todos los branches possibles de errores y tu si quieres lo simplificas con un else (_)... Madre mia. Que malos programadores de verdad. Que ademas podrías hacer un into_error() o into_reply() y que te ponga el status code automaticamente... Imaginaos que mal diseñado esta toda esta API que no se puede hacer nada de esto...
async fn handle_rejection(err: Rejection) -> Result<impl Reply, Infallible> {
err.into_reply()
}
Y asi cubres ell 99% de los casos de uso de la gente normal. Devolver un error con el estatus code que toca sin body o un body default. Y si quieres meter body haces el match...
match err {
rejection::NotFound => reply::with_status("not found bro", StatusCode::NOT_FOUND)
_ => err.into_reply()
}
Estos errores son completamente inútiles si no puedes devolver:
404, "your request for object id: 1234 was not found in volume: abc_def"
Así que vas a tener que hacer custom errors para todos los errores que te surgen o sera un mensaje genérico, que mejor dejar vacío...
Lo voy a dejar así por si tenéis curiosidad:
Ya digo que para ir bien, todos nuestros handlers deben SI o SI devolver custom errors con información de runtime para devolver a usuario o no valen para nada... asi que esta función la usare para matchear mis errores y construir la respuesta... la libreria de rejection de Warp no vale absolutamente para nada, es una mierda.
Fijaros que yo en mi codigo, si pasa un error de aplicación, ya puedo construir la respuesta cuando ocurre como hice aqui:
if lock_keys.contains(&key) {
return Ok(warp::http::Response::builder()
.status(warp::http::StatusCode::CONFLICT)
.body(String::new()));
}
Estos recovery solo deben usarse para cosas excepcionales que no has podido recoger y limpiar como el caso que digo que pete algo mientras escribimos en un volume y pete el codigo... En fin, libreria inutil donde las alla, no hay por donde cogerla.
@desu illuminanos, entonces por que existe toda esta parafernalia? pues para detectar automaticamente estos errores:
.and(warp::body::content_length_limit(64))
.and(warp::body::json::<SearchQuery>())
Si falla el content length limit o el json a SearchQuery, son un ejemplo de internet, pues te devolverá el error que toca, pero tu como usuario de esta API no puedes hacer un cagado.
Asi que quizás me lo cargo todo y hago un map del error y fuera...