Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error Handling Examples #388

Open
bradfier opened this issue Jan 18, 2020 · 28 comments
Open

Error Handling Examples #388

bradfier opened this issue Jan 18, 2020 · 28 comments

Comments

@bradfier
Copy link

bradfier commented Jan 18, 2020

It's possible I'm just not following the rejections example well enough, but what is the correct way of indicating that a handler (called with and_then()) is fallible, and returning the error from that method in a reply?

For an example of the behaviour I'm trying to replicate, in Actix or Rouille, handler functions are defined as returning something resembling a Result<GoodReply, BadReply>, both of which can be expected to produce an HTTP response.

e.g a simple index handler which should generate a 500 if the templating fails:

async fn index(tmpl: web::Data<tera::Tera>) -> Result<HttpResponse, Error> {
    let html = tmpl
        .render("index.html", &tera::Context::new())
        .map_err(|_| HttpResponse::InternalServerError("Failed to render template")))?;

    Ok(HttpResponse::Ok().body(html))
}

Reading the docs for Warp, it seems like a Rejection should be used to indicate that this filter could not or should not accept the request, is this the case? And if so, is there an idiomatic way to replicate the behaviour I've sketched above? I can go down the road of matching on results from fallible functions in my handlers, and doing an early return warp::reply(...), but then I lose out on the ergonomics offered by the ? operator inside the function.

@jxs
Copy link
Collaborator

jxs commented Jan 19, 2020

Hi, if I understand correctly you question, to return early with a custom Rejection you have to create an Error, which has to impl warp::reject::Reject, and wrap it under warp::reject::custom.
taking your example:

enum MyError {
    TeraError(tera::Error)
}
impl warp::reject::Reject for MyError {}

async fn index(tmpl: web::Data<tera::Tera>) -> Result<HttpResponse, Rejection> {
    let html = tmpl
        .render("index.html", &tera::Context::new())
        .map_err(|err| warp::reject::custom(MyError::TeraError(err)))?;

    Ok(HttpResponse::Ok().body(html))
}

you can then convert this Rejection into a Reply using the recover filter, or else this will be returned as a 500 Internal Server Error.
the rejections example shows how to recover from a Rejection and convert it to a Reply according to each custom Rejection

@sondr3
Copy link

sondr3 commented Jan 19, 2020

I have/had the same question regarding the todos example, how would I idiomatically handle errors in the mod handlers functions?

@seanmonstar
Copy link
Owner

Rejections are meant to say a Filter couldn't fulfill its preconditions, but maybe another Filter can. If a filter is otherwise fully matched, and an error occurs in your business logic, it's probably not correct to reject with the error. In that case, you'd want to construct a Reply that describes your error.

In the todos example, once the request has gotten through the filters and arrived to the handler function, it chooses to return responses with the correct status codes instead of rejecting.

It may be worth adding some pattern to ease that?

@qoh
Copy link

qoh commented Jan 21, 2020

Rejections are meant to say a Filter couldn't fulfill its preconditions, but maybe another Filter can. If a filter is otherwise fully matched, and an error occurs in your business logic, it's probably not correct to reject with the error. In that case, you'd want to construct a Reply that describes your error.

I think this is what I needed to read, I've apparently been going about this incorrectly the whole time.

It may be worth adding some pattern to ease that?

I would definitely be interested in that, though I'm not sure what it would look like. I've only been using warp for a few weeks (tracking master pre-0.2), and I was initially relatively confused about what the proper way to deal with routes failing was. Rejections seemed silly since I wouldn't want to try the entire rest of the tree just to have it fail to match.

Despite that, I've mistakenly (apparently; thanks for the clarification, the repo being small enough to watch fully has been helpful) found myself rejecting with warp::reject::custom(ApiError::Variant) so far, and looking for ApiError in a recover filter. Maybe the annoying verbosity of warp::reject::custom should have ticked me off 😄.

Without rejections + recover however, it does somewhat seem like there's a gap when it comes to unified error handling? Of course, every individual route could call a function to build an error response, but having this everywhere:

let foo = match get_foo().await {
    Ok(v) => v,
    Err(e) => return Ok(some_error_response(e)),
}

... would be very unpleasant, and even worse than .map_err(|e| warp::reject::custom(SomeVariant(e)))?. Additionally, unified logging of errors becomes a lot harder!

In some ways it feels natural, in others silly, to have the route handler filters (by choice) be <Extract = (Result<Reply, MyError>,), Rejection = !>, and then using an .ok_or_else() adapter on the top-level filter to translate MyError to another Reply. Not sure if this would be a good idea.

@qoh
Copy link

qoh commented Jan 22, 2020

I'm also uncertain how one should handle filters required for a route? If I match a path and decide on its route, but need a database connection to handle the request, I don't want to pointlessly try matching other paths if I fail to acquire one; I want to send a 500 response immediately. A thorough explanation of how what kinds of errors should be and are handled would be very appreciated.

@kud1ing kud1ing mentioned this issue Jan 27, 2020
@cjbassi
Copy link

cjbassi commented Feb 23, 2020

I've been able to get the error handling working with a custom error type by converting my error to a Rejection in the handler and then converting it to a Response in the recover filter, but the ergonomics are not great. For instance, there is a lot of .map_err(MyError::from)? instead of just ?.

It seems like it's a common pattern to not return a Rejection once you get to the handler, and instead return error responses. It would be nice if there was a way to return a Result in the handlers that implements Reply . Unfortunately, I was not able to get this to work because you cannot impl an external trait for a custom Result that is aliased to std::result::Result.

edit: Would it be possible to add an impl Reply for Result types in warp itself? Or maybe add a custom result type in warp for this?

@Draphar
Copy link

Draphar commented Mar 23, 2020

I've solved this by creating this Result wrapper, but I agree it would be nice to have this functionality in warp. Not being able to return Result, the error variant of which triggers an Internal Server Error, is quite unintuitive.

use http::StatusCode;
use std::error::Error;
use warp::reply::Reply;
use warp::reply::{html, with_status, Response};

const INTERNAL_SERVER_ERROR: &'static str = "Internal server error";

pub struct ReplyResult<T, E>(Result<T, E>);

impl<T: Reply, E: Send + Error> Reply for ReplyResult<T, E> {
    fn into_response(self) -> Response {
        match self.0 {
            Ok(x) => x.into_response(),
            Err(e) => {
                warn!("Filter failed: {}", e);
                with_status(
                    html(INTERNAL_SERVER_ERROR),
                    StatusCode::INTERNAL_SERVER_ERROR,
                )
                .into_response()
            }
        }
    }
}

impl<T, E> From<Result<T, E>> for ReplyResult<T, E> {
    fn from(value: Result<T, E>) -> ReplyResult<T, E> {
        ReplyResult(value)
    }
}

#[inline]
pub fn catch_error<T, E>(result: Result<T, E>) -> ReplyResult<T, E> { ReplyResult(result) }

It can be used by simply adding a single map with the function path:

use std::io::{Error, ErrorKind};
let route = warp::path!("guess" / isize)
    .map(|n| match n {
        0 => Ok("Secret unlocked"),
        _ => Err(Error::from(ErrorKind::Other))
    })
    .map(catch_error);

@cjbassi
Copy link

cjbassi commented Mar 23, 2020

Btw #458 implements Reply for Result in warp and I've been using it successfully in https://github.com/cjbassi/rust-warp-realworld-backend.

@jasonish
Copy link

jasonish commented Apr 6, 2020

In the todos example, once the request has gotten through the filters and arrived to the handler function, it chooses to return responses with the correct status codes instead of rejecting.

It may be worth adding some pattern to ease that?

I landed here looking for a pattern where I could use ?; to return an error from my handlers, but not go the rejection route, as there is no sense moving onto another handler, so some pattern to handle this would be very nice.

@nickbabcock
Copy link

To add to the other code examples heres: another technique that I've found useful is to create your own pseudo-try macro:

macro_rules! warp_try {
    ($expr:expr) => {
        match $expr {
            Ok(val) => val,
            Err(err) => {
                return Ok(err.into_response());
            }
        }
    };
}

Then tips for usage:

  • terminating endpoints return Result<warp::reply::Response, std::convert::Infallible> to signal that the endpoint will not reject. It may be return a 500 error code but no other route should be used.
  • Create a custom error type that implements warp::Reply so that into_response is implemented

The quickest of usage:

pub async fn my_endpoint(
    db: DbPool,
) -> Result<warp::reply::Response, std::convert::Infallible> {
    let conn = db.get()
        .await
        .map_err(MyServerError::DatabaseInvalidConnection);

    // Return a 500 error if the database pool is overly contended.
    // MyServerError implements `warp::Reply`
    let conn = warp_try!(conn);

    // ... do something with the connection ...

    Ok(warp::reply().into_response())
}

@sidju
Copy link

sidju commented Jul 23, 2020

My current solution is to use PR #458. It adds a .map_async in addition to implementing Reply if both sides of Result implement reply.

With that just implement reply on your error (that logs and converts it into a user facing error message) and run the handlers with .map_async.

Just change the warp line in your Cargo.toml to "warp = { git = "https://github.com/cjbassi/warp.git", branch = "error"}" to start using it now (no major feature losses for now, hoping for it to be merged before any occur).

@rkusa
Copy link
Sponsor

rkusa commented Jan 12, 2021

I agree with what has been mentioned before, it would be nice

  • if there would be a way to not always go through all filters (like having a conclusive filter that is the immediate response no matter where in the chain it occurs), and
  • to be able to use ? (which currently implies rejecting).

Speaking from the experience of using warp for quite a while now (and I love it, great engineering work!). For almost all API routes, once I get past the path filter, I don't want other filters to get executed at all (though I still think the default of trying the other filters is good). This does not only apply to the actual business logic of the route, but also to filters that are part of it (that define its parameters). Considering the following example (the comments describe what I'd like to achieve):

let a = warp::post() // if this fails, continue with `b`
    .and(path!("upload")) // if this fails, continue with `b`
    .and(authenticated()) // if this fails, don't continue with any other filter
    .and(warp::body::content_length_limit(1024 * 1024 * 10)) // if this fails, don't continue with any other filter
    .and(warp::multipart::form()) // if this fails, don't continue with any other filter
    .and_then(routes::upload); // if this fails, don't continue with any other filter
let b = warp::get()// ...
a.or(b)

If path!("upload") was successful, but one of the three succeeding filters (authentication, content length limit and multipart form) rejects, I want it to stop right there and don't try executing any other filter/route. I of course also want to stop executing other filters if routes::upload itself rejected (using rejects there due to the ergonomics of ?).

Fortunately, I've found a way to achieve this with built-in filters utilizing the current state of rejects:

let a = warp::post().and(
    // Everything starting here will be covered by the `recover` below
    path!("upload")
        // return a custom error if the path did not match so we can uniquely identify this case
        .or_else(|_| async { Err(reject::custom(PathMismatch)) })
        .and(authenticated())
        .and(warp::body::content_length_limit(1024 * 1024 * 10))
        .and(warp::multipart::form())
        .and_then(routes::upload)
        // The recover will handle all rejects that happened starting with `path!`
        .recover(recover),
);

Here is a reduced version of my recover function:

async fn recover(err: warp::Rejection) -> Result<impl warp::Reply, warp::Rejection> {
    if err.find::<PathMismatch>().is_some() {
        // This is the only case where we actually want to reject and continue with the other
        // filters in the filter chain. Otherwise, the execution would stop at the first `path`
        // that did not match the request.
        Err(reject::not_found())
    } else if let Some(ref err) = err.find::<error::ClientError>() {
        // An example of a custom error type I am using to return structured errors
        Ok(
            warp::reply::with_status(warp::reply::json(err), StatusCode::BAD_REQUEST)
                .into_response(),
        )
    } else if let Some(ref err) = err.find::<error::ServerError>() {
        // Another custom error type, this time for server errors
        log::error!("{}", err);
        Ok(StatusCode::INTERNAL_SERVER_ERROR.into_response())
    } else if err.find::<warp::reject::PayloadTooLarge>().is_some() {
        // An example of converting a rejection of a built-in filter
        // (`warp::body::content_length_limit` in this case) into a structured error
        Ok(warp::reply::with_status(
            warp::reply::json(&error::client_error(
                "file_size",
                "File size limit of 10MB exceeded",
            )),
            StatusCode::PAYLOAD_TOO_LARGE,
        )
        .into_response())
    } else {
        // My business logic always returns either one of my custom error types `ClientError`
        // and `ServerError`, so I safely assume that all other rejects are due to filters that
        // extract data from the request, which is why I simply return a 404 in those cases.
        // It is important to return an `Ok` here, because again, no other filters should be
        // executed afterwards.
        Ok(StatusCode::NOT_FOUND.into_response())
    }
}

#[derive(Debug)]
struct PathMismatch;

impl reject::Reject for PathMismatch {}

Maybe this helps others to achieve their intended behaviour.

@kaj
Copy link
Contributor

kaj commented Jan 14, 2021

I agree with what has been mentioned before, it would be nice

  • if there would be a way to not always go through all filters (like having a conclusive filter that is the immediate response no matter where in the chain it occurs), and

  • to be able to use ? (which currently implies rejecting).

The PR #458 delivers both these objectives in a nice and clear way.

@rkusa
Copy link
Sponsor

rkusa commented Jan 14, 2021

The PR #458 delivers both these objectives in a nice and clear way.

Yes indeed - there is only one thing the PR does not handle that I also wanted to achieve (but this was not highlighted in my bullet point list):

  • the filters that lead up to the route handler should be conclusive too (except the method, and path, so basically everything that rejects after the path should immediately send a corresponding response to the request)

@luke-brown
Copy link

luke-brown commented Feb 1, 2021

let a = warp::post().and(
// Everything starting here will be covered by the recover below
path!("upload")
// return a custom error if the path did not match so we can uniquely identify this case
.or_else(|_| async { Err(reject::custom(PathMismatch)) })
.and(authenticated())
...
if err.find::().is_some() {

Would you be able to skip the or_else(... Path_Mismatch) and corresponding part of the recover() handler by instead pulling the path!() filter out of the inner filter chain? As in: let a = warp::post().and(path!("upload")).and(authenticated().and(...)), where recover() is at the end of that inner filter chain that begins with authenticated() -- rather than a chain straight from path!() to recover().

I agree this subject would be great for more examples. I've gone down the same road of building logic trees with more than two levels of or()/and(), with recover() at the end of the inner "leaf" filter chains. It works, yet that pattern seems not to be emphasized much in the docs/examples. It looks like users may have trouble with internal errors and rejections (and unwanted route re-matching attempts) by virtue of using two-level trees with a single top-level recover(). Wouldn't inner recover()s also mitigate some of the trouble behind #451 (the related issues in its comments)? I'm happy to contribute to examples if this understanding is considered correctly warpy.

@rkusa
Copy link
Sponsor

rkusa commented Feb 1, 2021

Would you be able to skip the or_else(... Path_Mismatch) and corresponding part of the recover() handler by instead pulling the path!() filter out of the inner filter chain? As in: let a = warp::post().and(path!("upload")).and(authenticated().and(...)), where recover() is at the end of that inner filter chain that begins with authenticated() -- rather than a chain straight from path!() to recover().

Yes, but only for routes where you don't have parameters inside the path. Once you have parameters, you need this part, so that the parameter correctly contributes to the .and_then() handler (which is why I am adding to all of my routes for consistency reasons).

@luke-brown
Copy link

Yes, but only for routes where you don't have parameters inside the path. Once you have parameters, you need this part, so that the parameter correctly contributes to the .and_then() handler (which is why I am adding to all of my routes for consistency reasons).

Interesting--yeah when values are extracted from the path, how about: for each route, collapsing the filters into a single chain that ends in recover(handle_only_app_errors), letting warp's internal path rejection fall through to the outer or()? The per-route recover()s can handle only the rejections produced by the per-route filters, like PayloadTooLarge, parsing and custom errors, and path rejections fall out as Err(err). Would that also cover it?

@rkusa
Copy link
Sponsor

rkusa commented Feb 2, 2021

@luke-brown I am afraid this isn't working either, but I am not entirely sure that I understood you correctly. Here is an example of how I understood your example. While the following will work

path!("users" / String / "picture")
    .and(
        authenticated()
            .and(warp::body::content_length_limit(1024 * 1024 * 10)) // 10 MB
            .and(warp::multipart::form())
            .and(state.clone()),
    )
    .and_then(routes::upload::upload_users_picture)
    .recover(recover)

adding an additional recover to the non-path filters will not

path!("users" / String / "picture")
    .and(
        authenticated()
            .and(warp::body::content_length_limit(1024 * 1024 * 10)) // 10 MB
            .and(warp::multipart::form())
            .and(state.clone())
            .recover(recover),
    )
    .and_then(routes::upload::upload_users_picture)
    .recover(recover)

This fails to compile. It looks like adding the recover before the and_then changes the expected handler signature from (std::string::String, i64, FormData, state::State) to (std::string::String, warp::generic::Either<(i64, FormData, state::State), (impl Reply,)>) (authenticated returns a i64, and state.clone() a state::State in this example).

@luke-brown
Copy link

luke-brown commented Feb 2, 2021

@rkusa Here's what I'm thinking should work, annotated with your original comments on what you'd like it to achieve:

let a = warp::post() // if this fails, continue with `b`
    .and(path!("upload")) // if this fails, continue with `b`
    .and(authenticated()) // if this fails, don't continue with any other filter
    .and(warp::body::content_length_limit(1024 * 1024 * 10)) // if this fails, don't continue with any other filter
    .and(warp::multipart::form()) // if this fails, don't continue with any other filter
    .and_then(routes::upload) // if this fails, don't continue with any other filter
    .recover(handle_upload_err);
let b = warp::get() // ...
    .and_then(do_b);
let routes = a.or(b).or(other);

With the upload-specific rejection handler doing this:

async fn handle_upload_err(err: Rejection) -> Result<impl warp::Reply, Rejection> {
    if err.is_not_found() {
        // Try other routes
        return Err(err);
    }

    // (edited post to add this: in case there are filters for other methods at the same path)
    if let Some(_) = err.find::<warp::reject::MethodNotAllowed>() {
        // Try other routes
        return Err(err);
    }

    // As long as an Ok Result response is returned here (presumably with an HTTP error code),
    // the upload route will be resolved.

    if let Some(AppError::Specific) = err.find() {
        error!("specific app error");
        return Ok(StatusCode::NOT_FOUND);
    }

    error!("unexpected upload error: {:?}", err);
    Ok(StatusCode::INTERNAL_SERVER_ERROR)
}

With that setup, something like a POST /upload with the wrong content type will result in that 500 "unexpected upload error", while a request with a different path and/or method will fall through to the rest of the routes in the or() filter.

It's essentially the same as your solution--I was only adding that I think it can be done this way without needing to add that custom PathMismatch rejection type: by attaching recover() to the filter chain that includes the path(), and handling every rejection in that chain except for the path() rejections. That other solution of moving everything after the path into a wrapped and() filter also seems nice, as long as it's possible to do so, i.e. whether the path() is extracting values.

@luke-brown
Copy link

Oh.. if you might have other methods at the same path, then that works by also passing through errors matching let Some(_) = err.find::<warp::reject::MethodNotAllowed>() in addition to err.is_not_found(). It seems the main problem with this solution is that it relies on knowing which rejections are possible from warp::path(), but it otherwise seems like a relatively low-friction way to get that routing behavior.

@rkusa
Copy link
Sponsor

rkusa commented Feb 13, 2021

Here's what I'm thinking should work

@luke-brown I think you are right, that should work, too. To not continue trying other routes if your own handlers returns a 404, You'd only have to make sure to not use https://docs.rs/warp/0.3.0/warp/reject/fn.not_found.html in your own routes and handlers (but this can be replaced with a custom error that is alter converted to a 404 too).

@benitogf
Copy link

hello I'm trying to handle a reject for websocket upgrade:

  let subscription = warp::path::full()
    .and(warp::ws())
    .and(subscription_pools)
    .and(subscription_db)
    .map(|full_path: warp::path::FullPath, ws: warp::ws::Ws, pools: Pools, db: Storage| {
      let key = String::from(rem_first_char(full_path.as_str()));
      // TODO: return 400 on invalid key subscription
      if key_valid(&key) {
         ws.on_upgrade(move |websocket| ws_reader(websocket, key, pools, db))
      } else {
        let obj: WriteMessage = WriteMessage{
          data: String::from("invalid key"),
        };
        warp::reply::with_status(warp::reply::json(&obj), http::StatusCode::BAD_REQUEST)
      }
    });

this gives the error:

`if` and `else` have incompatible types

expected opaque type, found struct `warp::reply::WithStatus`

note: expected type `impl warp::Reply`
       found struct `warp::reply::WithStatus<warp::reply::Json>`

is there a way to achieve this?

@boxdot
Copy link
Contributor

boxdot commented Mar 16, 2021

@benitogf It seems to me that this is not easy to solve at the moment. What is really missing, is an Either type which implements Reply. Since the Reply is sealed, it is only possible to add it upstream. Another possibility is to use recover, but then you need to reimplement the whole logic from the shipped Rejection.

@rkusa
Copy link
Sponsor

rkusa commented Mar 16, 2021

@benitogf Adding a .into_response() to all responses might solve your issue:

      if key_valid(&key) {
-        ws.on_upgrade(move |websocket| ws_reader(websocket, key, pools, db))
+        ws.on_upgrade(move |websocket| ws_reader(websocket, key, pools, db)).into_response()
      } else {
        let obj: WriteMessage = WriteMessage{
          data: String::from("invalid key"),
        };
-       warp::reply::with_status(warp::reply::json(&obj), http::StatusCode::BAD_REQUEST)
+       warp::reply::with_status(warp::reply::json(&obj), http::StatusCode::BAD_REQUEST).into_response()
      }

@benitogf
Copy link

@benitogf Adding a .into_response() to all responses might solve your issue:

      if key_valid(&key) {
-        ws.on_upgrade(move |websocket| ws_reader(websocket, key, pools, db))
+        ws.on_upgrade(move |websocket| ws_reader(websocket, key, pools, db)).into_response()
      } else {
        let obj: WriteMessage = WriteMessage{
          data: String::from("invalid key"),
        };
-       warp::reply::with_status(warp::reply::json(&obj), http::StatusCode::BAD_REQUEST)
+       warp::reply::with_status(warp::reply::json(&obj), http::StatusCode::BAD_REQUEST).into_response()
      }

this method is not available on version 0.3?

no method named `into_response` found for opaque type `impl warp::Reply` in the current scope

method not found in `impl warp::Reply`

help: items from traits can only be used if the trait is in scope
no method named `into_response` found for struct `warp::reply::WithStatus<warp::reply::Json>` in the current scope

method not found in `warp::reply::WithStatus<warp::reply::Json>`

help: items from traits can only be used if the trait is in scope

Think that @boxdot comment is right, go this error after trying to use a a result and warp::reject:

  fn _subscribe(full_path: warp::path::FullPath, ws: warp::ws::Ws, pools: Pools, db: Storage) -> Result<impl warp::Reply, warp::Rejection> {
    let key = String::from(rem_first_char(full_path.as_str()));
    // TODO: return 400 on invalid key subscription
    if key_valid(&key) {
      let reply = ws.on_upgrade(move |websocket| ws_reader(websocket, key, pools, db));
      Ok(warp::reply::with_header(reply, "sec-websocket-protocol", "proto"))
    } else {
      let obj: WriteMessage = WriteMessage{
        data: String::from("invalid key"),
      };
      // Ok(warp::reply::with_status(warp::reply::json(&obj), http::StatusCode::BAD_REQUEST).into_response())
      Err(warp::reject())
    }
  }

  // routes
  let subscription = warp::path::full()
    .and(warp::ws())
    .and(subscription_pools)
    .and(subscription_db)
    .map(_subscribe);
the trait bound `std::result::Result<impl warp::Reply, warp::Rejection>: warp::Reply` is not satisfied

the trait `warp::Reply` is not implemented for `std::result::Result<impl warp::Reply, warp::Rejection>`

help: the following implementations were found:
        <std::result::Result<T, warp::http::Error> as warp::Reply>
note: required because of the requirements on the impl of `warp::Reply` for `(std::result::Result<impl warp::Reply, warp::Rejection>,)`
note: 4 redundant requirements hidden
note: required because of the requirements on the impl of `warp::Reply` for `(warp::generic::Either<(warp::generic::Either<(std::result::Result<impl warp::Reply, warp::Rejection>,), (impl warp::Reply,)>,), (warp::reply::WithStatus<warp::reply::Json>,)>,)`

@rkusa
Copy link
Sponsor

rkusa commented Mar 17, 2021

@benitogf

this method is not available on version 0.3?

no method named `into_response` found for opaque type `impl warp::Reply` in the current scope

method not found in `impl warp::Reply`

help: items from traits can only be used if the trait is in scope

It is, you are probably just missing a use warp::Reply (as the error suggests).

@benitogf
Copy link

thanks @rkusa using into_response works 👍

@Stargateur
Copy link

Stargateur commented Jul 7, 2021

unless I miss something the use of Result is very miss leading

mildbyte added a commit to splitgraph/seafowl that referenced this issue Aug 16, 2022
- A handler function, instead of returning a Warp reply/rejection, returns a
  `Result<Reply, ApiError>.`
  - This is because rejections are meant to say "this filter can't handle this
    request, but maybe some other can" (see
seanmonstar/warp#388 (comment)).
  - A rejection means Warp will fall through to another filter and ultimately
    hit a rejection handler, with people reporting rejections take way too long
to process with more routes.
  - In our case, the error in our handler function is final and we also would
    like to be able to use the ? operator to bail out of the handler if an error
exists, which using a Result type handles for us.

- ApiError knows how to convert itself to an HTTP response + status code
  (error-specific), allowing us to implement Reply for ApiError.

- We can't implement `Reply` for `Result<Reply, Reply>` (we didn't make the
  `Result` type), so we have to add a final function `into_response` that
converts our `Result` into a Response. We won't need to do this when
seanmonstar/warp#909 is merged:

```
.then(my_handler_func)
.map(into_response)
```
mildbyte added a commit to splitgraph/seafowl that referenced this issue Aug 16, 2022
- A handler function, instead of returning a Warp reply/rejection, returns a
  `Result<Reply, ApiError>.`
  - This is because rejections are meant to say "this filter can't handle this
    request, but maybe some other can" (see
seanmonstar/warp#388 (comment)).
  - A rejection means Warp will fall through to another filter and ultimately
    hit a rejection handler, with people reporting rejections take way too long
to process with more routes.
  - In our case, the error in our handler function is final and we also would
    like to be able to use the ? operator to bail out of the handler if an error
exists, which using a Result type handles for us.

- ApiError knows how to convert itself to an HTTP response + status code
  (error-specific), allowing us to implement Reply for ApiError.

- We can't implement `Reply` for `Result<Reply, Reply>` (we didn't make the
  `Result` type), so we have to add a final function `into_response` that
converts our `Result` into a Response. We won't need to do this when
seanmonstar/warp#909 is merged:

```
.then(my_handler_func)
.map(into_response)
```
mildbyte added a commit to splitgraph/seafowl that referenced this issue Aug 17, 2022
- A handler function, instead of returning a Warp reply/rejection, returns a
  `Result<Reply, ApiError>.`
  - This is because rejections are meant to say "this filter can't handle this
    request, but maybe some other can" (see
seanmonstar/warp#388 (comment)).
  - A rejection means Warp will fall through to another filter and ultimately
    hit a rejection handler, with people reporting rejections take way too long
to process with more routes.
  - In our case, the error in our handler function is final and we also would
    like to be able to use the ? operator to bail out of the handler if an error
exists, which using a Result type handles for us.

- ApiError knows how to convert itself to an HTTP response + status code
  (error-specific), allowing us to implement Reply for ApiError.

- We can't implement `Reply` for `Result<Reply, Reply>` (we didn't make the
  `Result` type), so we have to add a final function `into_response` that
converts our `Result` into a Response. We won't need to do this when
seanmonstar/warp#909 is merged:

```
.then(my_handler_func)
.map(into_response)
```
mildbyte added a commit to splitgraph/seafowl that referenced this issue Aug 17, 2022
- A handler function, instead of returning a Warp reply/rejection, returns a
  `Result<Reply, ApiError>.`
  - This is because rejections are meant to say "this filter can't handle this
    request, but maybe some other can" (see
seanmonstar/warp#388 (comment)).
  - A rejection means Warp will fall through to another filter and ultimately
    hit a rejection handler, with people reporting rejections take way too long
to process with more routes.
  - In our case, the error in our handler function is final and we also would
    like to be able to use the ? operator to bail out of the handler if an error
exists, which using a Result type handles for us.

- ApiError knows how to convert itself to an HTTP response + status code
  (error-specific), allowing us to implement Reply for ApiError.

- We can't implement `Reply` for `Result<Reply, Reply>` (we didn't make the
  `Result` type), so we have to add a final function `into_response` that
converts our `Result` into a Response. We won't need to do this when
seanmonstar/warp#909 is merged:

```
.then(my_handler_func)
.map(into_response)
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.