3 ms·
I like the way Axum for Rust handles this. All response handlers return something that implements IntoResponse. Lots of types implement IntoResponse, thus mak
by dralley 4y ago
I like the way Axum for Rust handles this. All response handlers return something that implements IntoResponse. Lots of types implement IntoResponse, thus making the simple cases really streamlined.
async fn create_user(
Json(payload): Json<CreateUser>,
) -> impl IntoResponse {
let user = User {
id: 1337,
username: payload.username,
};
// this will be converted into a JSON response
// with a status code of `201 Created`
(StatusCode::CREATED, Json(user))
}
If the handler is complex enough that branches can't all return the same type, you can just add .into_response() to each return site, and now you're just returning a response, which also implements IntoResponse (as a no-op).
This avoids the problem discussed in this article while also avoiding much of the boilerplate of manually building responses.
- masklinn 4y agoI’ve not yet gotten to use it, but I understand Axum also implements IntoResponse for Result<A: IntoResponse, B: IntoResponse>, which is very convenient for the sort of error code of the above. It’s one of my biggest annoyance with Warp: it only implements Reply for `Result<T, http::error::Error>`, so if you have a faillible handler with more complicated error types (e.g. with a json body, or even a 200 response for RPC handlers) you’re in the awkward position of having to join the paths back (usually by converting everything to a Response by hand), which makes mistakes easier.
- zaarn 4y agoCorrect, I spent about half an hour implementing IntoResponse for my own project's error type, but now I can just use normal error handling and I get presented with an error page on any issues. It's quite magic (not really, very streamlined).
- pram 4y agoThat is what Gin does.
- maccard 4y agoGin is excellent. It's the right balance of batteries included, sensible defaults, and black magic
- suremarc 4y agoYou don't return anything in Gin.
- unscaled 4y agoNo, that's exactly the opposite of what it does. The article above explicitly criticizes gin for not returning a value. Echo is closer to the pattern described above, but: 1. Echo lets you return an error response, but doesn't let you customize how the error is rendered _in a modular way_. You can only have a single global custom error handler that decides how to serialize the errors, and it needs to have knowledge of how to properly serialize every kind of error in your app. I have to admit this is generally good enough for most of the apps, and I don't think you can do any better in Go, considering it doesn't have type classes like Rust. The best that you can do is to let errors implement a CustomHttpRenderer interface on their own and try to cast into it — but this is already doable with a custom error renderer in Echo. 2. Echo doesn't let you return a non-error Response from the handler - instead it's written directly to the ReponseWriter. This approach makes writing unit tests several orders of magnitude harder and also means you can forget to write a successful response in some cases. You can also both write a response directly and return an error, which cannot result in a valid behavior (Echo will take the safe route and just ignore the error code and do nothing).
- pram 4y agoAxum isn't returning anything, IntoResponse outputs to HTTP. The error type in a handler is Infallible.
- merb 4y agothe problem is that when you use NO_CONTENT that you prolly can violate the http spec. (you can do that with both)
- zaarn 4y agoA lot of HTTP frameworks let you violate the HTTP spec to a surprising degree. Though with Rust you can just double check the return data with a middleware for Axum.
- laumars 4y agoThere are Go HTTP libraries that behave that way too. This article only refers to the one in standard library.
- scriptsmith 4y agoThank you! I ran into this branching problem the other day, ended up trying to return a complicated boxed future and gave up. In retrospect it makes sense to do it the way you mentioned. I think because I’m not as confident with traits and rust’s brand of async I tried to get it to work the same way I would in other languages, which is to mess with the function’s return type myself, rather than let the compiler figure it out.
- Winsaucerer 4y agoYep, it's a good tip, something I'd been hitting too!