Improve State and Router docs (#1543)

This commit is contained in:
jimmycuadra 2022-11-22 00:08:39 -08:00 committed by GitHub
parent 7d0bb28876
commit 6771729d27
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 128 additions and 40 deletions

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
is because `Router` only allows a single fallback.
[combining-stateful-routers]: crate::extract::State#combining-stateful-routers

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
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
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`.
# 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
`Router`. If you need to nest a `Router` with a different state type you can
use [`Router::with_state`] and [`Router::nest_service`]:
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.
```rust
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.
If you want to compose axum services with different types of state, use
[`Router::nest_service`].
# Panics
@ -193,3 +165,4 @@ for more details.
[`OriginalUri`]: crate::extract::OriginalUri
[fallbacks]: Router::fallback
[combining-stateful-routers]: crate::extract::State#combining-stateful-routers

View file

@ -46,6 +46,77 @@ use std::{
/// # 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`
///
/// ```

View file

@ -165,11 +165,12 @@
//!
//! # Sharing state with handlers
//!
//! It is common to share some state between handlers for example to share a
//! pool of database connections or clients to other services.
//! It is common to share some state between handlers. For example, a
//! pool of database connections or clients to other services may need to
//! be shared.
//!
//! The three most common ways of doing that are:
//! - Using the [`State`] extractor.
//! - Using the [`State`] extractor
//! - Using request extensions
//! - Using closure captures
//!

View file

@ -212,6 +212,41 @@ where
}
/// 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]
pub fn nest_service<T>(self, path: &str, svc: T) -> Self
where