This was working a couple of days ago, but now when I send a POST to /dog I get a 404 and I can't figure out why. Here is the code that defines that route:
If I change the order of the routes, it fixes requests to create_dog
but it breaks requests to get_dogs
. So it seems like a rejection from a filter earlier in the list is preventing a later one from running, but I don't know why.
@@ -123,9 +124,9 @@ async fn main() {
});
//TODO: Learn how to get this to use TLS/HTTPS.
- let routes = get_dogs
+ let routes = create_dog
.or(get_dog)
- .or(create_dog)
+ .or(get_dogs)
.or(update_dog)
.or(delete_dog);
They both work if I use this ordering, though I didn't test the remaining routes:
let routes = get_dogs
.or(create_dog)
.or(get_dog)
.or(update_dog)
.or(delete_dog);
Aha! The problem is that you added .recover(not_found)
to the get_dog
filter, which means that this filter never fails, so no filters after it get a chance to run.
You may instead want to use recover
only at the end of your list of routes:
--- a/warp/src/main.rs
+++ b/warp/src/main.rs
@@ -70,8 +70,7 @@ async fn main() {
} else {
Err(warp::reject::not_found())
}
- })
- .recover(not_found);
+ });
async fn not_found(_err: Rejection) -> Result<impl warp::Reply, Rejection> {
Ok(StatusCode::NOT_FOUND)
@@ -127,6 +126,7 @@ async fn main() {
.or(get_dog)
.or(create_dog)
.or(update_dog)
- .or(delete_dog);
+ .or(delete_dog)
+ .recover(not_found);
warp::serve(routes).run(([127, 0, 0, 1], 1234)).await;
}
If I do that, will that make it so any error in any route will be reported as a 404? The problem I'm trying to address with the recover
is that without that warp will report a 405 Method Not Allowed when I send GET /dog/some-bad-id instead of a 404.
If you want the filter to handle the request by returning an error, rather than rejecting it and letting later filters have a try, you can return Ok(some_error_reply)
instead of Err(some_rejection)
. For example:
let get_dog = warp::path!("dog" / String)
.and(warp::get())
.and(with_state(state.clone()))
.and_then(handle_get_dog);
async fn handle_get_dog(id: String, state: State) -> Result<impl Reply, Infallible> {
let dog_map = state.read();
if let Some(dog) = dog_map.get(&id) {
Ok(with_status(json(&dog), StatusCode::OK))
} else {
Ok(with_status(json(&"not found"), StatusCode::NOT_FOUND))
}
}
This seems to be the approach that the todos example uses.
I definitely prefer that over the .recover(
approach. Thanks so much!