This reworks axum's docs in an attempt to make things easier to find. Previously I wasn't a fan of those docs for the same topic were spread across the root module docs and more specific places like types and methods. This changes it such that the root module docs only gives a high level introduction to a topic, perhaps with a small example, and then link to other places where all the details are. This means `Router` is now the single place to learn about routing, and etc for the topics like handlers and error handling.
5.2 KiB
Add another route to the router.
path is a string of path segments separated by /. Each segment
can be either static, a capture, or a wildcard.
service is the [Service] that should receive the request if the path matches
path. service will commonly be a handler wrapped in a method router like
get. See handler for more details
on handlers.
Static paths
Examples:
//foo/users/123
If the incoming request matches the path exactly the corresponding service will be called.
Captures
Paths can contain segments like /:key which matches any single segment and
will store the value captured at key.
Examples:
/:key/users/:id/users/:id/tweets
Captures can be extracted using Path. See its
documentation for more details.
It is not possible to create segments that only match some types like numbers or regular expression. You must handle that manually in your handlers.
MatchedPath can be used to extract the matched
path rather than the actual path.
Wildcards
Paths can end in /*key which matches all segments and will store the segments
captured at key.
Examples:
/*key/assets/*path/:id/:repo/*tree
Wildcard captures can also be extracted using Path.
More examples
use axum::{Router, routing::{get, delete}, extract::Path};
let app = Router::new()
.route("/", get(root))
.route("/users", get(list_users).post(create_user))
.route("/users/:id", get(show_user))
.route("/api/:version/users/:id/action", delete(do_users_action))
.route("/assets/*path", get(serve_asset));
async fn root() {}
async fn list_users() {}
async fn create_user() {}
async fn show_user(Path(id): Path<u64>) {}
async fn do_users_action(Path((version, id)): Path<(String, u64)>) {}
async fn serve_asset(Path(path): Path<String>) {}
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
Routing to any [Service]
axum also supports routing to general [Service]s:
use axum::{
Router,
body::Body,
routing::service_method_routing as service,
error_handling::HandleErrorExt,
http::{Request, StatusCode},
};
use tower_http::services::ServeFile;
use http::Response;
use std::{convert::Infallible, io};
use tower::service_fn;
let app = Router::new()
.route(
// Any request to `/` goes to a service
"/",
// Services who's response body is not `axum::body::BoxBody`
// can be wrapped in `axum::service::any` (or one of the other routing filters)
// to have the response body mapped
service::any(service_fn(|_: Request<Body>| async {
let res = Response::new(Body::from("Hi from `GET /`"));
Ok::<_, Infallible>(res)
}))
)
.route(
"/foo",
// This service's response body is `axum::body::BoxBody` so
// it can be routed to directly.
service_fn(|req: Request<Body>| async move {
let body = Body::from(format!("Hi from `{} /foo`", req.method()));
let body = axum::body::box_body(body);
let res = Response::new(body);
Ok::<_, Infallible>(res)
})
)
.route(
// GET `/static/Cargo.toml` goes to a service from tower-http
"/static/Cargo.toml",
service::get(ServeFile::new("Cargo.toml"))
// though we must handle any potential errors
.handle_error(|error: io::Error| {
(
StatusCode::INTERNAL_SERVER_ERROR,
format!("Unhandled internal error: {}", error),
)
})
);
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
Routing to arbitrary services in this way has complications for backpressure
([Service::poll_ready]). See the [service_method_routing] module for more
details.
Panics
Panics if the route overlaps with another route:
use axum::{routing::get, Router};
let app = Router::new()
.route("/", get(|| async {}))
.route("/", get(|| async {}));
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
The static route /foo and the dynamic route /:key are not considered to
overlap and /foo will take precedence.
Take care when using [Router::nest] as it behaves like a wildcard route.
Therefore this setup panics:
use axum::{routing::get, Router};
let app = Router::new()
// this is similar to `/api/*`
.nest("/api", get(|| async {}))
// which overlaps with this route
.route("/api/users", get(|| async {}));
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
Also panics if path is empty.
Nesting
route cannot be used to nest Routers. Instead use [Router::nest].
Attempting to will result in a panic:
use axum::{routing::get, Router};
let app = Router::new().route(
"/",
Router::new().route("/foo", get(|| async {})),
);
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };