-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
feat(rejection): add rejection as a response extension #1932
feat(rejection): add rejection as a response extension #1932
Conversation
While this doesn't have to clone the rejection, it still has to allocate for the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Thanks for the PR! I think this is an interesting idea and is something I've considered in the past. However there are a few things I don't like about it: I main gripe I have is that you have to remember to go update a middleware when you introduce a new rejection. If you forget you might accidentally be sending a wrong rejection response. You could argue that rejections are quite implicit already, which is true. That is why the current ways to customize rejections are all "local to the handler". You just have to look at the handler to know that you're handling the rejections correctly, maybe via your own wrapper types. You can then use something like clippy's It's also not clear how we should handle rejections that are part of multiple composite rejections, such as You could also consider not inserting the rejection directly but maybe something that has the status and body, but that's already available on the response itself, and you loose typed data such as |
Hi! Thank you both for your insight.
I see rejections as uncommon cases, having this allocation occur only when rejection takes place doesn't seem
This looks pretty similar to the process of creating a new custom extractor and remembering to add it to clippy's
I could try inserting the composite rejection inside
I agree that this might be the best approach for small to medium-sized projects. For large workspaces, however, |
That's true. Though wrappers have the advantage that you don't pay for the middleware or the extension unless you opt-in to using them. The downside is that it's a bit of boilerplate but probably not much different from a middleware.
Yeah I think you're right that that is a common way to get consistency across services, but you could just as well share wrapper types with custom rejections. Overall I think it's an interesting idea but I'm not sure how to make the judgement call. Might have to spend some time thinking about it and comparing the alternatives. |
Thank you for the quick response nonetheless, I'll leave the decision to close this up to you! |
I'm still not sure this is a great or that much better than the alternatives, so I think I'l err on the side of caution and not merge this, for now at least. |
Thank you for the consideration nonetheless! |
Motivation
There's no current way to handle rejections in a global manner, which adds a certain layer of
complexity requiring the developer to create its own extractor, or use
WithRejection
everysingle time.
It'd be nice to have a way to do this without needing to configure every route separately, to
behave in a way that is expected from all of them.
This is a follow-up to my #1116 (comment).
Solution
The idea was to insert the rejection into the Response extensions when calling
IntoResponse
.This is done via modifying the
define_rejection
macro fromaxum-core
. No clones are requiredas
self
is never consumed inside this macro.To simplify the process of checking the rejection type, I've added an optional
composite
meta tothe macro invocation, if present, the rejection is inserted as its composite type, e.g.,
JsonSyntaxError
is inserted as
JsonRejection::JsonSyntaxError
.This allows a middleware to check whether a Response contains rejection information or not, and
respond accordingly:
This solution doesn't provide a ready way to change how the rejection behaves but gives the devs
more information, and they are allowed to do whatever they want with it. Extensions seem like the
right place as a rejection is metadata regarding a response.
Other ideas
We can even go as far as creating a
RejectionHandler<Rejection>
layer which receives afn(Rejection) -> Response
, but I don't think it is necessary.