Improve State and Router docs (#1543)

This commit is contained in:
jimmycuadra
2022-11-22 09:08:39 +01:00
committed by GitHub
parent 7d0bb28876
commit 6771729d27
5 changed files with 128 additions and 40 deletions
+9 -1
View File
@@ -37,7 +37,15 @@ let app = Router::new()
# }; # };
``` ```
## Panics # Merging routers with state
When combining [`Router`]s with this function, each [`Router`] must have the
same type of state. See ["Combining stateful routers"][combining-stateful-routers]
for details.
# Panics
- If two routers that each have a [fallback](Router::fallback) are merged. This - If two routers that each have a [fallback](Router::fallback) are merged. This
is because `Router` only allows a single fallback. is because `Router` only allows a single fallback.
[combining-stateful-routers]: crate::extract::State#combining-stateful-routers
+9 -36
View File
@@ -1,4 +1,4 @@
Nest a [`Service`] at some path. Nest a [`Router`] at some path.
This allows you to break your application into smaller pieces and compose This allows you to break your application into smaller pieces and compose
them together. them together.
@@ -64,7 +64,7 @@ let app = Router::new().nest("/:version/api", users_api);
# }; # };
``` ```
# Differences to wildcard routes # Differences from wildcard routes
Nested routes are similar to wildcard routes. The difference is that Nested routes are similar to wildcard routes. The difference is that
wildcard routes still see the whole URI whereas nested routes will have wildcard routes still see the whole URI whereas nested routes will have
@@ -147,42 +147,14 @@ let app = Router::new()
Here requests like `GET /api/not-found` will go to `api_fallback`. Here requests like `GET /api/not-found` will go to `api_fallback`.
# Nesting a router with a different state type # Nesting routers with state
By default `nest` requires a `Router` with the same state type as the outer When combining [`Router`]s with this function, each [`Router`] must have the
`Router`. If you need to nest a `Router` with a different state type you can same type of state. See ["Combining stateful routers"][combining-stateful-routers]
use [`Router::with_state`] and [`Router::nest_service`]: for details.
```rust If you want to compose axum services with different types of state, use
use axum::{ [`Router::nest_service`].
Router,
routing::get,
extract::State,
};
#[derive(Clone)]
struct InnerState {}
#[derive(Clone)]
struct OuterState {}
async fn inner_handler(state: State<InnerState>) {}
let inner_router = Router::new()
.route("/bar", get(inner_handler))
.with_state(InnerState {});
async fn outer_handler(state: State<OuterState>) {}
let app = Router::new()
.route("/", get(outer_handler))
.nest_service("/foo", inner_router)
.with_state(OuterState {});
# let _: axum::routing::RouterService = app;
```
Note that the inner router will still inherit the fallback from the outer
router.
# Panics # Panics
@@ -193,3 +165,4 @@ for more details.
[`OriginalUri`]: crate::extract::OriginalUri [`OriginalUri`]: crate::extract::OriginalUri
[fallbacks]: Router::fallback [fallbacks]: Router::fallback
[combining-stateful-routers]: crate::extract::State#combining-stateful-routers
+71
View File
@@ -46,6 +46,77 @@ use std::{
/// # let _: axum::routing::RouterService = app; /// # let _: axum::routing::RouterService = app;
/// ``` /// ```
/// ///
/// ## Combining stateful routers
///
/// Multiple [`Router`]s can be combined with [`Router::nest`] or [`Router::merge`]
/// When combining [`Router`]s with one of these methods, the [`Router`]s must have
/// the same state type. Generally, this can be inferred automatically:
///
/// ```
/// use axum::{Router, routing::get, extract::State};
///
/// #[derive(Clone)]
/// struct AppState {}
///
/// let state = AppState {};
///
/// // create a `Router` that will be nested within another
/// let api = Router::new()
/// .route("/posts", get(posts_handler));
///
/// let app = Router::new()
/// .nest("/api", api)
/// .with_state(state);
///
/// async fn posts_handler(State(state): State<AppState>) {
/// // use `state`...
/// }
/// # let _: axum::routing::RouterService = app;
/// ```
///
/// However, if you are composing [`Router`]s that are defined in separate scopes,
/// you may need to annotate the [`State`] type explicitly:
///
/// ```
/// use axum::{Router, RouterService, routing::get, extract::State};
///
/// #[derive(Clone)]
/// struct AppState {}
///
/// fn make_app() -> RouterService {
/// let state = AppState {};
///
/// Router::new()
/// .nest("/api", make_api())
/// .with_state(state) // the outer Router's state is inferred
/// }
///
/// // the inner Router must specify its state type to compose with the
/// // outer router
/// fn make_api() -> Router<AppState> {
/// Router::new()
/// .route("/posts", get(posts_handler))
/// }
///
/// async fn posts_handler(State(state): State<AppState>) {
/// // use `state`...
/// }
/// # let _: axum::routing::RouterService = make_app();
/// ```
///
/// In short, a [`Router`]'s generic state type defaults to `()`
/// (no state) unless [`Router::with_state`] is called or the value
/// of the generic type is given explicitly.
///
/// It's also possible to combine multiple axum services with different state
/// types. See [`Router::nest_service`] for details.
///
/// [`Router`]: crate::Router
/// [`Router::merge`]: crate::Router::merge
/// [`Router::nest_service`]: crate::Router::nest_service
/// [`Router::nest`]: crate::Router::nest
/// [`Router::with_state`]: crate::Router::with_state
///
/// # With `MethodRouter` /// # With `MethodRouter`
/// ///
/// ``` /// ```
+4 -3
View File
@@ -165,11 +165,12 @@
//! //!
//! # Sharing state with handlers //! # Sharing state with handlers
//! //!
//! It is common to share some state between handlers for example to share a //! It is common to share some state between handlers. For example, a
//! pool of database connections or clients to other services. //! pool of database connections or clients to other services may need to
//! be shared.
//! //!
//! The three most common ways of doing that are: //! The three most common ways of doing that are:
//! - Using the [`State`] extractor. //! - Using the [`State`] extractor
//! - Using request extensions //! - Using request extensions
//! - Using closure captures //! - Using closure captures
//! //!
+35
View File
@@ -212,6 +212,41 @@ where
} }
/// Like [`nest`](Self::nest), but accepts an arbitrary `Service`. /// Like [`nest`](Self::nest), but accepts an arbitrary `Service`.
///
/// While [`nest`](Self::nest) requires [`Router`]s with the same type of
/// state, you can use this method to combine [`Router`]s with different
/// types of state:
///
/// ```
/// use axum::{
/// Router,
/// routing::get,
/// extract::State,
/// };
///
/// #[derive(Clone)]
/// struct InnerState {}
///
/// #[derive(Clone)]
/// struct OuterState {}
///
/// async fn inner_handler(state: State<InnerState>) {}
///
/// let inner_router = Router::new()
/// .route("/bar", get(inner_handler))
/// .with_state(InnerState {});
///
/// async fn outer_handler(state: State<OuterState>) {}
///
/// let app = Router::new()
/// .route("/", get(outer_handler))
/// .nest_service("/foo", inner_router)
/// .with_state(OuterState {});
/// # let _: axum::routing::RouterService = app;
/// ```
///
/// Note that the inner router will still inherit the fallback from the outer
/// router.
#[track_caller] #[track_caller]
pub fn nest_service<T>(self, path: &str, svc: T) -> Self pub fn nest_service<T>(self, path: &str, svc: T) -> Self
where where