diff --git a/axum-core/src/extract/from_ref.rs b/axum-core/src/extract/from_ref.rs index cee9d3377a..bdfa7dd07e 100644 --- a/axum-core/src/extract/from_ref.rs +++ b/axum-core/src/extract/from_ref.rs @@ -5,10 +5,9 @@ /// /// See [`State`] for more details on how library authors should use this trait. /// -/// This trait can be derived using `#[derive(axum_macros::FromRef)]`. +/// This trait can be derived using `#[derive(FromRef)]`. /// /// [`State`]: https://docs.rs/axum/0.6/axum/extract/struct.State.html -/// [`#[derive(axum_macros::FromRef)]`]: https://docs.rs/axum-macros/latest/axum_macros/derive.FromRef.html // NOTE: This trait is defined in axum-core, even though it is mainly used with `State` which is // defined in axum. That allows crate authors to use it when implementing extractors. pub trait FromRef { diff --git a/axum/src/docs/routing/with_state.md b/axum/src/docs/routing/with_state.md new file mode 100644 index 0000000000..ecaa1432b8 --- /dev/null +++ b/axum/src/docs/routing/with_state.md @@ -0,0 +1,236 @@ +Provide the state for the router. + +```rust +use axum::{Router, routing::get, extract::State}; + +#[derive(Clone)] +struct AppState {} + +let routes = Router::new() + .route("/", get(|State(state): State| async { + // use state + })) + .with_state(AppState {}); + +# async { +axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) + .serve(routes.into_make_service()) + .await; +# }; +``` + +# Returning routers with states from functions + +When returning `Router`s from functions it is generally recommend not set the +state directly: + +```rust +use axum::{Router, routing::get, extract::State}; + +#[derive(Clone)] +struct AppState {} + +// Don't call `Router::with_state` here +fn routes() -> Router { + Router::new() + .route("/", get(|_: State| async {})) +} + +// Instead do it before you run the server +let routes = routes().with_state(AppState {}); + +# async { +axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) + .serve(routes.into_make_service()) + .await; +# }; +``` + +If you do need to provide the state, and you're _not_ nesting/merging the router +into another router, then return `Router` without any type parameters: + +```rust +# use axum::{Router, routing::get, extract::State}; +# #[derive(Clone)] +# struct AppState {} +# +// Don't return `Router` +fn routes(state: AppState) -> Router { + Router::new() + .route("/", get(|_: State| async {})) + .with_state(state) +} + +let routes = routes(AppState {}); + +# async { +axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) + .serve(routes.into_make_service()) + .await; +# }; +``` + +This is because we can only call `Router::into_make_service` on `Router<()>`, +not `Router`. See below for more details about why that is. + +Note that the state defaults to `()` so `Router` and `Router<()>` is the same. + +If you are nesting/merging the router it is recommended to use a generic state +type on the resulting router: + +```rust +# use axum::{Router, routing::get, extract::State}; +# #[derive(Clone)] +# struct AppState {} +# +fn routes(state: AppState) -> Router { + Router::new() + .route("/", get(|_: State| async {})) + .with_state(state) +} + +let routes = Router::new().nest("/api", routes(AppState {})); + +# async { +axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) + .serve(routes.into_make_service()) + .await; +# }; +``` + +# What `S` in `Router` means + +`Router` means a router that is _missing_ a state of type `S` to be able to +handle requests. It does _not_ mean a `Router` that _has_ a state of type `S`. + +For example: + +```rust +# use axum::{Router, routing::get, extract::State}; +# #[derive(Clone)] +# struct AppState {} +# +// A router that _needs_ an `AppState` to handle requests +let router: Router = Router::new() + .route("/", get(|_: State| async {})); + +// Once we call `Router::with_state` the router isn't missing +// the state anymore, because we just provided it +// +// Therefore the router type becomes `Router<()>`, i.e a router +// that is not missing any state +let router: Router<()> = router.with_state(AppState {}); + +// Only `Router<()>` has the `into_make_service` method. +// +// You cannot call `into_make_service` on a `Router` +// because it is still missing an `AppState`. +# async { +axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) + .serve(router.into_make_service()) + .await; +# }; +``` + +Perhaps a little counter intuitively, `Router::with_state` doesn't always return a +`Router<()>`. Instead you get to pick what the new missing state type is: + +```rust +# use axum::{Router, routing::get, extract::State}; +# #[derive(Clone)] +# struct AppState {} +# +let router: Router = Router::new() + .route("/", get(|_: State| async {})); + +// When we call `with_state` we're able to pick what the next missing state type is. +// Here we pick `String`. +let string_router: Router = router.with_state(AppState {}); + +// That allows us to add new routes that uses `String` as the state type +let string_router = string_router + .route("/needs-string", get(|_: State| async {})); + +// Provide the `String` and choose `()` as the new missing state. +let final_router: Router<()> = string_router.with_state("foo".to_owned()); + +// Since we have a `Router<()>` we can run it. +# async { +axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) + .serve(final_router.into_make_service()) + .await; +# }; +``` + +This why this returning `Router` after calling `with_state` doesn't +work: + +```rust,compile_fail +# use axum::{Router, routing::get, extract::State}; +# #[derive(Clone)] +# struct AppState {} +# +// This wont work because we're returning a `Router` +// i.e. we're saying we're still missing an `AppState` +fn routes(state: AppState) -> Router { + Router::new() + .route("/", get(|_: State| async {})) + .with_state(state) +} + +let app = routes(AppState {}); + +// We can only call `Router::into_make_service` on a `Router<()>` +// but `app` is a `Router` +# async { +axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) + .serve(app.into_make_service()) + .await; +# }; +``` + +Instead return `Router<()>` since we have provided all the state needed: + +```rust +# use axum::{Router, routing::get, extract::State}; +# #[derive(Clone)] +# struct AppState {} +# +// We've provided all the state necessary so return `Router<()>` +fn routes(state: AppState) -> Router<()> { + Router::new() + .route("/", get(|_: State| async {})) + .with_state(state) +} + +let app = routes(AppState {}); + +// We can now call `Router::into_make_service` +# async { +axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) + .serve(app.into_make_service()) + .await; +# }; +``` + +# A note about performance + +If you need a `Router` that implements `Service` but you don't need any state (perhaps +you're making a library that uses axum internally) then it is recommended to call this +method before you start serving requests: + +```rust +use axum::{Router, routing::get}; + +let app = Router::new() + .route("/", get(|| async { /* ... */ })) + // even though we don't need any state, call `with_state(())` anyway + .with_state(()); +# let _: Router = app; +``` + +This is not required but it gives axum a chance to update some internals in the router +which may impact performance and reduce allocations. + +Note that [`Router::into_make_service`] and [`Router::into_make_service_with_connect_info`] +do this automatically. diff --git a/axum/src/routing/mod.rs b/axum/src/routing/mod.rs index 367ced338b..e7b717ea41 100644 --- a/axum/src/routing/mod.rs +++ b/axum/src/routing/mod.rs @@ -103,8 +103,6 @@ where pub(crate) const NEST_TAIL_PARAM: &str = "__private__axum_nest_tail_param"; pub(crate) const NEST_TAIL_PARAM_CAPTURE: &str = "/*__private__axum_nest_tail_param"; -impl Router<(), B> where B: HttpBody + Send + 'static {} - impl Router where B: HttpBody + Send + 'static, @@ -396,61 +394,7 @@ where self } - /// Provide the state for the router. - /// - /// This method returns a router with a different state type. This can be used to nest or merge - /// routers with different state types. See [`Router::nest`] and [`Router::merge`] for more - /// details. - /// - /// # Implementing `Service` - /// - /// This can also be used to get a `Router` that implements [`Service`], since it only does so - /// when the state is `()`: - /// - /// ``` - /// use axum::{ - /// Router, - /// body::Body, - /// http::Request, - /// }; - /// use tower::{Service, ServiceExt}; - /// - /// #[derive(Clone)] - /// struct AppState {} - /// - /// // this router doesn't implement `Service` because its state isn't `()` - /// let router: Router = Router::new(); - /// - /// // by providing the state and setting the new state to `()`... - /// let router_service: Router<()> = router.with_state(AppState {}); - /// - /// // ...makes it implement `Service` - /// # async { - /// router_service.oneshot(Request::new(Body::empty())).await; - /// # }; - /// ``` - /// - /// # A note about performance - /// - /// If you need a `Router` that implements `Service` but you don't need any state (perhaps - /// you're making a library that uses axum internally) then it is recommended to call this - /// method before you start serving requests: - /// - /// ``` - /// use axum::{Router, routing::get}; - /// - /// let app = Router::new() - /// .route("/", get(|| async { /* ... */ })) - /// // even though we don't need any state, call `with_state(())` anyway - /// .with_state(()); - /// # let _: Router = app; - /// ``` - /// - /// This is not required but it gives axum a chance to update some internals in the router - /// which may impact performance and reduce allocations. - /// - /// Note that [`Router::into_make_service`] and [`Router::into_make_service_with_connect_info`] - /// do this automatically. + #[doc = include_str!("../docs/routing/with_state.md")] pub fn with_state(self, state: S) -> Router { let routes = self .routes