mirror of
https://github.com/tokio-rs/axum.git
synced 2026-08-25 00:00:23 +02:00
Add Router::route_layer (#474)
This addresses something thats been bothering me for some time: Most middleware need to run regardless if the request matches a route or not. For example you don't wanna skip logging for unmatched requests.
However middleware such as authorization only make sense to run for matching requests. This previously wasn't possible to express and you'd have to manually apply the middleware to each handler. Consider this:
```rust
Router::new()
.route("/foo", get(|| async {}))
.layer(RequireAuthorizationLayer::bearer("password"));
```
Calling `GET /foo` with an invalid token would receive `401 Unauthorized` as expected however calling some unknown route like `GET /not-found` would also return `401 Unauthorized`. I think this is unexpected and have seen a few users ask questions about it.
It happened because the 404 you'd otherwise see is generated by a fallback service stored on `Router`. When adding a layer to the router the layer would also be applied to the fallback, which in the case of auth means the fallback would never be called for unauthorized requests.
I think what axum does today is the right default however I still think we should support this somehow. Especially since [`extractor_middleware`](https://docs.rs/axum/0.3.1/axum/extract/fn.extractor_middleware.html) is mainly useful for auth but it doesn't work great today due to this gotcha.
This PR proposes adding `Router::layer_on_matching_route` which only applies layers to routes, not the fallback, which fixes the issue. I'm not a big fan of the name `layer_on_matching_route`, would like something shorter, but I think it communicates the purpose decently.
The generics are a bit different since the request body used on the routes and the fallback must match, so layers that changes the request body type are not compatible with `layer_on_matching_route`. Such middleware are very rare so that should be fine.
This commit is contained in:
+40
-10
@@ -223,19 +223,16 @@ where
|
||||
}
|
||||
|
||||
#[doc = include_str!("../docs/routing/layer.md")]
|
||||
pub fn layer<L, LayeredReqBody, LayeredResBody>(self, layer: L) -> Router<LayeredReqBody>
|
||||
pub fn layer<L, NewReqBody, NewResBody>(self, layer: L) -> Router<NewReqBody>
|
||||
where
|
||||
L: Layer<Route<B>>,
|
||||
L::Service: Service<
|
||||
Request<LayeredReqBody>,
|
||||
Response = Response<LayeredResBody>,
|
||||
Error = Infallible,
|
||||
> + Clone
|
||||
L::Service: Service<Request<NewReqBody>, Response = Response<NewResBody>, Error = Infallible>
|
||||
+ Clone
|
||||
+ Send
|
||||
+ 'static,
|
||||
<L::Service as Service<Request<LayeredReqBody>>>::Future: Send + 'static,
|
||||
LayeredResBody: http_body::Body<Data = Bytes> + Send + 'static,
|
||||
LayeredResBody::Error: Into<BoxError>,
|
||||
<L::Service as Service<Request<NewReqBody>>>::Future: Send + 'static,
|
||||
NewResBody: http_body::Body<Data = Bytes> + Send + 'static,
|
||||
NewResBody::Error: Into<BoxError>,
|
||||
{
|
||||
let layer = ServiceBuilder::new()
|
||||
.layer_fn(Route::new)
|
||||
@@ -249,7 +246,7 @@ where
|
||||
let route = Layer::layer(&layer, route);
|
||||
(id, route)
|
||||
})
|
||||
.collect::<HashMap<RouteId, Route<LayeredReqBody>>>();
|
||||
.collect();
|
||||
|
||||
let fallback = self.fallback.map(|svc| Layer::layer(&layer, svc));
|
||||
|
||||
@@ -260,6 +257,39 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = include_str!("../docs/routing/route_layer.md")]
|
||||
pub fn route_layer<L, NewResBody>(self, layer: L) -> Self
|
||||
where
|
||||
L: Layer<Route<B>>,
|
||||
L::Service: Service<Request<B>, Response = Response<NewResBody>, Error = Infallible>
|
||||
+ Clone
|
||||
+ Send
|
||||
+ 'static,
|
||||
<L::Service as Service<Request<B>>>::Future: Send + 'static,
|
||||
NewResBody: http_body::Body<Data = Bytes> + Send + 'static,
|
||||
NewResBody::Error: Into<BoxError>,
|
||||
{
|
||||
let layer = ServiceBuilder::new()
|
||||
.layer_fn(Route::new)
|
||||
.layer(MapResponseBodyLayer::new(box_body))
|
||||
.layer(layer);
|
||||
|
||||
let routes = self
|
||||
.routes
|
||||
.into_iter()
|
||||
.map(|(id, route)| {
|
||||
let route = Layer::layer(&layer, route);
|
||||
(id, route)
|
||||
})
|
||||
.collect();
|
||||
|
||||
Router {
|
||||
routes,
|
||||
node: self.node,
|
||||
fallback: self.fallback,
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = include_str!("../docs/routing/fallback.md")]
|
||||
pub fn fallback<T>(mut self, svc: T) -> Self
|
||||
where
|
||||
|
||||
Reference in New Issue
Block a user