mirror of
https://github.com/tokio-rs/axum.git
synced 2026-08-28 00:00:20 +02:00
Support turning any Service into a MakeService (#1302)
* Add `ServiceExt` * changelog
This commit is contained in:
@@ -363,6 +363,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- **fixed:** Annotate panicking functions with `#[track_caller]` so the error
|
- **fixed:** Annotate panicking functions with `#[track_caller]` so the error
|
||||||
message points to where the user added the invalid route, rather than
|
message points to where the user added the invalid route, rather than
|
||||||
somewhere internally in axum ([#1248])
|
somewhere internally in axum ([#1248])
|
||||||
|
- **added:** Add `ServiceExt` with methods for turning any `Service` into a
|
||||||
|
`MakeService` similarly to `Router::into_make_service` ([#1302])
|
||||||
|
|
||||||
[#1077]: https://github.com/tokio-rs/axum/pull/1077
|
[#1077]: https://github.com/tokio-rs/axum/pull/1077
|
||||||
[#1086]: https://github.com/tokio-rs/axum/pull/1086
|
[#1086]: https://github.com/tokio-rs/axum/pull/1086
|
||||||
@@ -375,6 +377,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
[#1248]: https://github.com/tokio-rs/axum/pull/1248
|
[#1248]: https://github.com/tokio-rs/axum/pull/1248
|
||||||
[#1272]: https://github.com/tokio-rs/axum/pull/1272
|
[#1272]: https://github.com/tokio-rs/axum/pull/1272
|
||||||
[#1301]: https://github.com/tokio-rs/axum/pull/1301
|
[#1301]: https://github.com/tokio-rs/axum/pull/1301
|
||||||
|
[#1302]: https://github.com/tokio-rs/axum/pull/1302
|
||||||
[#924]: https://github.com/tokio-rs/axum/pull/924
|
[#924]: https://github.com/tokio-rs/axum/pull/924
|
||||||
|
|
||||||
# 0.5.15 (9. August, 2022)
|
# 0.5.15 (9. August, 2022)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
- [Routing to services/middleware and backpressure](#routing-to-servicesmiddleware-and-backpressure)
|
- [Routing to services/middleware and backpressure](#routing-to-servicesmiddleware-and-backpressure)
|
||||||
- [Accessing state in middleware](#accessing-state-in-middleware)
|
- [Accessing state in middleware](#accessing-state-in-middleware)
|
||||||
- [Passing state from middleware to handlers](#passing-state-from-middleware-to-handlers)
|
- [Passing state from middleware to handlers](#passing-state-from-middleware-to-handlers)
|
||||||
|
- [Rewriting request URI in middleware](#rewriting-request-uri-in-middleware)
|
||||||
|
|
||||||
# Intro
|
# Intro
|
||||||
|
|
||||||
@@ -557,6 +558,47 @@ let app = Router::new()
|
|||||||
automatically moved to response extensions. You need to manually do that for the
|
automatically moved to response extensions. You need to manually do that for the
|
||||||
extensions you need.
|
extensions you need.
|
||||||
|
|
||||||
|
# Rewriting request URI in middleware
|
||||||
|
|
||||||
|
Middleware added with [`Router::layer`] will run after routing. That means it
|
||||||
|
cannot be used to run middleware that rewrites the request URI. By the time the
|
||||||
|
middleware runs the routing is already done.
|
||||||
|
|
||||||
|
The workaround is to wrap the middleware around the entire `Router` (this works
|
||||||
|
because `Router` implements [`Service`]):
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use tower::Layer;
|
||||||
|
use axum::{
|
||||||
|
Router,
|
||||||
|
ServiceExt, // for `into_make_service`
|
||||||
|
response::Response,
|
||||||
|
middleware::Next,
|
||||||
|
http::Request,
|
||||||
|
};
|
||||||
|
|
||||||
|
async fn rewrite_request_uri<B>(req: Request<B>, next: Next<B>) -> Response {
|
||||||
|
// ...
|
||||||
|
# next.run(req).await
|
||||||
|
}
|
||||||
|
|
||||||
|
// this can be any `tower::Layer`
|
||||||
|
let middleware = axum::middleware::from_fn(rewrite_request_uri);
|
||||||
|
|
||||||
|
let app = Router::new();
|
||||||
|
|
||||||
|
// apply the layer around the whole `Router`
|
||||||
|
// this way the middleware will run before `Router` receives the request
|
||||||
|
let app_with_middleware = middleware.layer(app);
|
||||||
|
|
||||||
|
# async {
|
||||||
|
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
|
||||||
|
.serve(app_with_middleware.into_make_service())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
# };
|
||||||
|
```
|
||||||
|
|
||||||
[`tower`]: https://crates.io/crates/tower
|
[`tower`]: https://crates.io/crates/tower
|
||||||
[`tower-http`]: https://crates.io/crates/tower-http
|
[`tower-http`]: https://crates.io/crates/tower-http
|
||||||
[tower-guides]: https://github.com/tower-rs/tower/tree/master/guides
|
[tower-guides]: https://github.com/tower-rs/tower/tree/master/guides
|
||||||
@@ -576,3 +618,4 @@ extensions you need.
|
|||||||
[request extensions]: https://docs.rs/http/latest/http/request/struct.Request.html#method.extensions
|
[request extensions]: https://docs.rs/http/latest/http/request/struct.Request.html#method.extensions
|
||||||
[Response extensions]: https://docs.rs/http/latest/http/response/struct.Response.html#method.extensions
|
[Response extensions]: https://docs.rs/http/latest/http/response/struct.Response.html#method.extensions
|
||||||
[`State`]: crate::extract::State
|
[`State`]: crate::extract::State
|
||||||
|
[`Service`]: tower::Service
|
||||||
|
|||||||
@@ -9,6 +9,11 @@ of routes.
|
|||||||
Note this differs from [`Handler::layer`](crate::handler::Handler::layer)
|
Note this differs from [`Handler::layer`](crate::handler::Handler::layer)
|
||||||
which adds a middleware to a single handler.
|
which adds a middleware to a single handler.
|
||||||
|
|
||||||
|
Middleware added with this method will run _after_ routing and thus cannot be
|
||||||
|
used to rewrite the request URI. See ["Rewriting request URI in
|
||||||
|
middleware"](crate::middleware#rewriting-request-uri-in-middleware) for more
|
||||||
|
details and a workaround.
|
||||||
|
|
||||||
# Example
|
# Example
|
||||||
|
|
||||||
Adding the [`tower::limit::ConcurrencyLimit`] middleware to a group of
|
Adding the [`tower::limit::ConcurrencyLimit`] middleware to a group of
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
pub(crate) mod request;
|
pub(crate) mod request;
|
||||||
pub(crate) mod request_parts;
|
pub(crate) mod request_parts;
|
||||||
|
pub(crate) mod service;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
use crate::{extract::connect_info::IntoMakeServiceWithConnectInfo, routing::IntoMakeService};
|
||||||
|
use tower_service::Service;
|
||||||
|
|
||||||
|
/// Extension trait that adds additional methods to any [`Service`].
|
||||||
|
pub trait ServiceExt<R>: Service<R> + Sized {
|
||||||
|
/// Convert this service into a [`MakeService`], that is a [`Service`] whose
|
||||||
|
/// response is another service.
|
||||||
|
///
|
||||||
|
/// This is commonly used when applying middleware around an entire [`Router`]. See ["Rewriting
|
||||||
|
/// request URI in middleware"] for more details.
|
||||||
|
///
|
||||||
|
/// [`MakeService`]: tower::make::MakeService
|
||||||
|
/// ["Rewriting request URI in middleware"]: crate::middleware#rewriting-request-uri-in-middleware
|
||||||
|
/// [`Router`]: crate::Router
|
||||||
|
fn into_make_service(self) -> IntoMakeService<Self>;
|
||||||
|
|
||||||
|
/// Convert this service into a [`MakeService`], that will store `C`'s
|
||||||
|
/// associated `ConnectInfo` in a request extension such that [`ConnectInfo`]
|
||||||
|
/// can extract it.
|
||||||
|
///
|
||||||
|
/// This enables extracting things like the client's remote address.
|
||||||
|
/// This is commonly used when applying middleware around an entire [`Router`]. See ["Rewriting
|
||||||
|
/// request URI in middleware"] for more details.
|
||||||
|
///
|
||||||
|
/// [`MakeService`]: tower::make::MakeService
|
||||||
|
/// ["Rewriting request URI in middleware"]: crate::middleware#rewriting-request-uri-in-middleware
|
||||||
|
/// [`Router`]: crate::Router
|
||||||
|
/// [`ConnectInfo`]: crate::extract::connect_info::ConnectInfo
|
||||||
|
fn into_make_service_with_connect_info<C>(self) -> IntoMakeServiceWithConnectInfo<Self, C>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, R> ServiceExt<R> for S
|
||||||
|
where
|
||||||
|
S: Service<R> + Sized,
|
||||||
|
{
|
||||||
|
fn into_make_service(self) -> IntoMakeService<Self> {
|
||||||
|
IntoMakeService::new(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_make_service_with_connect_info<C>(self) -> IntoMakeServiceWithConnectInfo<Self, C> {
|
||||||
|
IntoMakeServiceWithConnectInfo::new(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-1
@@ -486,4 +486,6 @@ pub use axum_core::{BoxError, Error};
|
|||||||
#[cfg(feature = "macros")]
|
#[cfg(feature = "macros")]
|
||||||
pub use axum_macros::debug_handler;
|
pub use axum_macros::debug_handler;
|
||||||
|
|
||||||
pub use self::ext_traits::{request::RequestExt, request_parts::RequestPartsExt};
|
pub use self::ext_traits::{
|
||||||
|
request::RequestExt, request_parts::RequestPartsExt, service::ServiceExt,
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user