Broke my warp demo

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;
 }
3 Likes

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.

3 Likes

I definitely prefer that over the .recover( approach. Thanks so much!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.