From dbab5a84b418882d1e2c6388c90bc321ea8ed5c6 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 22 Aug 2021 15:56:56 +0200 Subject: [PATCH] Expand middleware docs (#239) Adds docs on - Commonly used middleware - Writing your own middleware - Links to tower's guides --- src/lib.rs | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 90118df0..a54ebc0e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,9 @@ //! - [To individual handlers](#to-individual-handlers) //! - [To groups of routes](#to-groups-of-routes) //! - [Error handling](#error-handling) +//! - [Applying multiple middleware](#applying-multiple-middleware) +//! - [Commonly used middleware](#commonly-used-middleware) +//! - [Writing your own middleware](#writing-your-own-middleware) //! - [Sharing state with handlers](#sharing-state-with-handlers) //! - [Required dependencies](#required-dependencies) //! - [Examples](#examples) @@ -605,7 +608,10 @@ //! # Applying middleware //! //! axum is designed to take full advantage of the tower and tower-http -//! ecosystem of middleware: +//! ecosystem of middleware. +//! +//! If you're new to tower we recommend you read its [guides][tower-guides] for +//! a general introduction to tower and its concepts. //! //! ## To individual handlers //! @@ -751,6 +757,116 @@ //! # }; //! ``` //! +//! ## Commonly used middleware +//! +//! [`tower::util`] and [`tower_http`] have a large collection of middleware that are compatible +//! with axum. Some commonly used are: +//! +//! ```rust,no_run +//! use axum::{ +//! body::{Body, BoxBody}, +//! handler::get, +//! http::{Request, Response}, +//! Router, +//! }; +//! use tower::{ +//! filter::AsyncFilterLayer, +//! util::AndThenLayer, +//! ServiceBuilder, +//! }; +//! use std::convert::Infallible; +//! use tower_http::trace::TraceLayer; +//! +//! let middleware_stack = ServiceBuilder::new() +//! // `TraceLayer` adds high level tracing and logging +//! .layer(TraceLayer::new_for_http()) +//! // `AsyncFilterLayer` lets you asynchronously transform the request +//! .layer(AsyncFilterLayer::new(map_request)) +//! // `AndThenLayer` lets you asynchronously transform the response +//! .layer(AndThenLayer::new(map_response)) +//! .into_inner(); +//! +//! async fn map_request(req: Request) -> Result, Infallible> { +//! Ok(req) +//! } +//! +//! async fn map_response(res: Response) -> Result, Infallible> { +//! Ok(res) +//! } +//! +//! let app = Router::new() +//! .route("/", get(|| async { /* ... */ })) +//! .layer(middleware_stack); +//! # async { +//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); +//! # }; +//! ``` +//! +//! Additionally axum provides [`extract::extractor_middleware()`] for converting any extractor into +//! a middleware. Among other things, this can be useful for doing authorization. See +//! [`extract::extractor_middleware()`] for more details. +//! +//! ## Writing your own middleware +//! +//! You can also write you own middleware by implementing [`tower::Service`]: +//! +//! ``` +//! use axum::{ +//! body::{Body, BoxBody}, +//! handler::get, +//! http::{Request, Response}, +//! Router, +//! }; +//! use futures::future::BoxFuture; +//! use tower::{Service, layer::layer_fn}; +//! use std::task::{Context, Poll}; +//! +//! #[derive(Clone)] +//! struct MyMiddleware { +//! inner: S, +//! } +//! +//! impl Service> for MyMiddleware +//! where +//! S: Service, Response = Response> + Clone + Send + 'static, +//! S::Future: Send + 'static, +//! ReqBody: Send + 'static, +//! ResBody: Send + 'static, +//! { +//! type Response = S::Response; +//! type Error = S::Error; +//! type Future = BoxFuture<'static, Result>; +//! +//! fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { +//! self.inner.poll_ready(cx) +//! } +//! +//! fn call(&mut self, mut req: Request) -> Self::Future { +//! println!("`MyMiddleware` called!"); +//! +//! // best practice is to clone the inner service like this +//! // see https://github.com/tower-rs/tower/issues/547 for details +//! let clone = self.inner.clone(); +//! let mut inner = std::mem::replace(&mut self.inner, clone); +//! +//! Box::pin(async move { +//! let res: Response = inner.call(req).await?; +//! +//! println!("`MyMiddleware` received the response"); +//! +//! Ok(res) +//! }) +//! } +//! } +//! +//! let app = Router::new() +//! .route("/", get(|| async { /* ... */ })) +//! .layer(layer_fn(|inner| MyMiddleware { inner })); +//! # async { +//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); +//! # }; +//! ``` +//! //! # Sharing state with handlers //! //! It is common to share some state between handlers for example to share a @@ -840,7 +956,9 @@ //! [`OriginalUri`]: crate::extract::OriginalUri //! [`Service`]: tower::Service //! [`Service::poll_ready`]: tower::Service::poll_ready +//! [`tower::Service`]: tower::Service //! [`handle_error`]: routing::Router::handle_error +//! [tower-guides]: https://github.com/tower-rs/tower/tree/master/guides #![warn( clippy::all,