1
0
Fork 0
mirror of https://github.com/tokio-rs/axum.git synced 2025-03-21 22:48:51 +01:00

Support turning any Service into a MakeService ()

* Add `ServiceExt`

* changelog
This commit is contained in:
David Pedersen 2022-08-22 22:03:48 +02:00 committed by GitHub
parent ab36e65449
commit fa51cf5266
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 98 additions and 1 deletions

View file

@ -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
message points to where the user added the invalid route, rather than
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
[#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
[#1272]: https://github.com/tokio-rs/axum/pull/1272
[#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
# 0.5.15 (9. August, 2022)

View file

@ -8,6 +8,7 @@
- [Routing to services/middleware and backpressure](#routing-to-servicesmiddleware-and-backpressure)
- [Accessing state in middleware](#accessing-state-in-middleware)
- [Passing state from middleware to handlers](#passing-state-from-middleware-to-handlers)
- [Rewriting request URI in middleware](#rewriting-request-uri-in-middleware)
# Intro
@ -557,6 +558,47 @@ let app = Router::new()
automatically moved to response extensions. You need to manually do that for the
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-http`]: https://crates.io/crates/tower-http
[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
[Response extensions]: https://docs.rs/http/latest/http/response/struct.Response.html#method.extensions
[`State`]: crate::extract::State
[`Service`]: tower::Service

View file

@ -9,6 +9,11 @@ of routes.
Note this differs from [`Handler::layer`](crate::handler::Handler::layer)
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
Adding the [`tower::limit::ConcurrencyLimit`] middleware to a group of

View file

@ -1,5 +1,6 @@
pub(crate) mod request;
pub(crate) mod request_parts;
pub(crate) mod service;
#[cfg(test)]
mod tests {

View file

@ -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)
}
}

View file

@ -486,4 +486,6 @@ pub use axum_core::{BoxError, Error};
#[cfg(feature = "macros")]
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,
};