warp: Error Handling Examples
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.
About this issue
- Original URL
- State: open
- Created 4 years ago
- Reactions: 24
- Comments: 28 (3 by maintainers)
Links to this issue
Commits related to this issue
- Add error handling / propagation - 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... — committed to splitgraph/seafowl by mildbyte 2 years ago
- Add error handling / propagation - 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... — committed to splitgraph/seafowl by mildbyte 2 years ago
- Add error handling / propagation - 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... — committed to splitgraph/seafowl by mildbyte 2 years ago
- Add error handling / propagation - 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... — committed to splitgraph/seafowl by mildbyte 2 years ago
Rejections are meant to say a
Filtercouldn’t fulfill its preconditions, but maybe anotherFiltercan. If a filter is otherwise fully matched, and an error occurs in your business logic, it’s probably not correct torejectwith the error. In that case, you’d want to construct aReplythat describes your error.In the
todosexample, once the request has gotten through the filters and arrived to thehandlerfunction, it chooses to return responses with the correct status codes instead of rejecting.It may be worth adding some pattern to ease that?
I think this is what I needed to read, I’ve apparently been going about this incorrectly the whole time.
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 forApiErrorin arecoverfilter. Maybe the annoying verbosity ofwarp::reject::customshould have ticked me off 😄.Without rejections +
recoverhowever, 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:… 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 translateMyErrorto anotherReply. Not sure if this would be a good idea.I agree with what has been mentioned before, it would be nice
?(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
pathfilter, 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):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 ifroutes::uploaditself 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:
Here is a reduced version of my
recoverfunction:Maybe this helps others to achieve their intended behaviour.
Hi, if I understand correctly you question, to return early with a custom
Rejectionyou have to create an Error, which has to implwarp::reject::Reject, and wrap it underwarp::reject::custom. taking your example: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. therejections exampleshows how to recover from a Rejection and convert it to a Reply according to each custom RejectionI 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.
I’ve solved this by creating this
Resultwrapper, but I agree it would be nice to have this functionality inwarp. Not being able to returnResult, the error variant of which triggers anInternal Server Error, is quite unintuitive.It can be used by simply adding a single map with the function path:
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.
@benitogf
It is, you are probably just missing a
use warp::Reply(as the error suggests).Btw #458 implements Reply for Result in warp and I’ve been using it successfully in https://github.com/cjbassi/rust-warp-realworld-backend.
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 tostd::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?