Allowing Handler
s to not return IntoResponse
#3229
Labels
C-musings
Category: musings about a better world
Handler
s to not return IntoResponse
#3229
Motivation
There have been requests to convert users' errors into responses in a middleware with access to state (#3211) or propagating errors without converting them into responses first (#2587).
Another request was to make sure every route uses an authorization extractor (#3221). This could be solved by this by having all handlers return an
AuthorizedIntoResponse
which could be created only with one of the authorization extractors.Other things this should allow for is returning objects that
impl Serialize
and converting them toResponse
s based onAccept-Encoding
.I've also sometimes wanted to be able to transform the returned value before converting it into an HTTP response.
Current State
Currently, each
Handler
must returnimpl IntoResponse
and the response is automatically immediately converted intoResponse
which can then be acted upon by middlewares.This architecture lets people to transform only already serialized textual or binary data inside an HTTP response. This is good for things like adding HTTP headers or compression but less so for other transformations like adding a common field to JSON payloads.
Similarly,
MethodRouter
expects each of its services to returnResponse
andPathRouter
(andRouterInner
) expects each of its inner services (usuallyMethodRouter
s) to also returnResponse
.Proposal
We can allow
Handler
s to return any type. SimilarlyMethodRouter
,PathRouter
, andRouter
can then allow any type to be created from aRequest
. In the end we want to callinto_response
on whatever is eventually returned, but that can happen as late as inServe
which would require that theRouter
's response type implementsIntoResponse
.Instead of generating
Handler
implementation like this:we can generate:
Of note is that the returned future resolves
Result<Res, Response>
instead of justRes
. This is because extractors can fail and we need to return something. There are other options for theErr
variant but this is the simplest solution for the prototype I made.Similarly, we can add another generic parameter to
MethodRouter
andRouter
signifying what type will the responses be.We would require all handlers in a given
MethodRouter
to resolve to the same type and allService
s inside aRouter
to also resolve to the same type. That type can be changed dynamically though using mapping layers.Drawbacks
Requiring that services under all router paths may be a bit restrictive and push people to just convert everything into
Response
s anyway. This would probably also introduce new hard-to-debug type errors.I also think this would be an advanced feature that most users would not need to use often.
So while we can have an
AdvancedRouter<S = (), Res = Response>
we can still exposeRouter<S = ()>
which would work the same way as theRouter
we have today. That way users can decide between the simpler more user-friendly version or the advanced more powerful version. This would make the API surface we need to maintain larger, but the functionality itself would be concentrated only in the general versions of the routers.Open Questions
How to handle extractor rejections. Converting them to
Response
s is simple but we could also return anenum
containing a variant for each extractor or something likeBox<dyn Rejection>
.How to handle default fallbacks. If
Router
returnsResponse
s, we have decided that returning an empty 404 response is a sane default. There may not be similar sane defaults for other types. We might require explicit fallbacks.Example
I've created a version of this (without having the current and advanced versions side by side) in this branch, where there's also a bit contrived example returning a custom struct which gets turned into
Response
in a later middleware.Alternatives
Some of the unlocked functionality can be achieved with response extensions. This would however allow users to use typechecking to make sure they did not overlook a handler where they forget to set the extension.
The text was updated successfully, but these errors were encountered: