diff --git a/axum/src/docs/routing/nest.md b/axum/src/docs/routing/nest.md index 5ec1df84..4ce0ad33 100644 --- a/axum/src/docs/routing/nest.md +++ b/axum/src/docs/routing/nest.md @@ -1,4 +1,4 @@ -Nest a group of routes (or 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,36 +64,6 @@ let app = Router::new().nest("/:version/api", users_api); # }; ``` -# Nesting services - -`nest` also accepts any [`Service`]. This can for example be used with -[`tower_http::services::ServeDir`] to serve static files from a directory: - -```rust -use axum::{ - Router, - routing::get_service, - http::StatusCode, - error_handling::HandleErrorLayer, -}; -use std::{io, convert::Infallible}; -use tower_http::services::ServeDir; - -// Serves files inside the `public` directory at `GET /public/*` -let serve_dir_service = get_service(ServeDir::new("public")) - .handle_error(|error: io::Error| async move { - ( - StatusCode::INTERNAL_SERVER_ERROR, - format!("Unhandled internal error: {}", error), - ) - }); - -let app = Router::new().nest_service("/public", serve_dir_service); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; -``` - # Differences to wildcard routes Nested routes are similar to wildcard routes. The difference is that @@ -103,18 +73,49 @@ the prefix stripped: ```rust use axum::{routing::get, http::Uri, Router}; +let nested_router = Router::new() + .route("/", get(|uri: Uri| async { + // `uri` will _not_ contain `/bar` + })); + let app = Router::new() .route("/foo/*rest", get(|uri: Uri| async { // `uri` will contain `/foo` })) - .nest_service("/bar", get(|uri: Uri| async { - // `uri` will _not_ contain `/bar` - })); + .nest("/bar", nested_router); # async { # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); # }; ``` +# Differences between `nest` and `nest_service` + +When [fallbacks] are called differs between `nest` and `nested_service`. Routers +nested with `nest` will delegate to the fallback if they don't have a matching +route, whereas `nested_service` will not. + +```rust +use axum::{Router, routing::{get, any}, handler::Handler}; + +let nested_router = Router::new().route("/users", get(|| async {})); + +let nested_service = Router::new().route("/app.js", get(|| async {})); + +async fn fallback() {} + +let app = Router::new() + .nest("/api", nested_router) + .nest_service("/assets", nested_service) + // the fallback is not called for request starting with `/bar` but will be + // called for requests starting with `/foo` if `nested_router` doesn't have + // a matching route + .fallback(fallback.into_service()); +# let _: Router = app; +``` + +Note that you would normally use [`tower_http::services::ServeDir`] for serving +static files and thus not calling `nest_service` with a `Router`. + # Panics - If the route overlaps with another route. See [`Router::route`] @@ -125,3 +126,4 @@ for more details. `Router` only allows a single fallback. [`OriginalUri`]: crate::extract::OriginalUri +[fallbacks]: Router::fallback diff --git a/axum/src/docs/routing/nest_service.md b/axum/src/docs/routing/nest_service.md new file mode 100644 index 00000000..bb61bcea --- /dev/null +++ b/axum/src/docs/routing/nest_service.md @@ -0,0 +1,40 @@ +Nest a [`Service`] at some path. + +`nest_service` behaves in the same way as `nest` in terms of + +- [How the URI changes](#how-the-uri-changes) +- [Captures from outer routes](#captures-from-outer-routes) +- [Differences to wildcard routes](#differences-to-wildcard-routes) + +But differs with regards to [fallbacks]. See ["Differences between `nest` and +`nest_service`"](#differences-between-nest-and-nest_service) for more details. + +# Example + +`nest_service` can for example be used with [`tower_http::services::ServeDir`] +to serve static files from a directory: + +```rust +use axum::{ + Router, + routing::get_service, + http::StatusCode, + error_handling::HandleErrorLayer, +}; +use std::{io, convert::Infallible}; +use tower_http::services::ServeDir; + +// Serves files inside the `public` directory at `GET /assets/*` +let serve_dir_service = get_service(ServeDir::new("public")) + .handle_error(|error: io::Error| async move { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Unhandled internal error: {}", error), + ) + }); + +let app = Router::new().nest_service("/assets", serve_dir_service); +# let _: Router = app; +``` + +[fallbacks]: Router::fallback diff --git a/axum/src/docs/routing/route.md b/axum/src/docs/routing/route.md index 15f02788..cd9d703e 100644 --- a/axum/src/docs/routing/route.md +++ b/axum/src/docs/routing/route.md @@ -51,6 +51,8 @@ Examples: - `/:id/:repo/*tree` Wildcard captures can also be extracted using [`Path`](crate::extract::Path). +Note that the leading slash is not included, i.e. for the route `/foo/*rest` and +the path `/foo/bar/baz` the value of `rest` will be `bar/baz`. # Accepting multiple methods diff --git a/axum/src/routing/mod.rs b/axum/src/routing/mod.rs index acb6a93f..44ba414a 100644 --- a/axum/src/routing/mod.rs +++ b/axum/src/routing/mod.rs @@ -169,7 +169,6 @@ where self } - // TODO(david): update docs #[doc = include_str!("../docs/routing/nest.md")] pub fn nest(mut self, mut path: &str, router: Router) -> Self { if path.is_empty() { @@ -218,7 +217,7 @@ where self } - /// TODO + #[doc = include_str!("../docs/routing/nest_service.md")] pub fn nest_service(mut self, mut path: &str, svc: T) -> Self where T: Service, Response = Response, Error = Infallible> + Clone + Send + 'static,