Struct axum::Router

source ·
pub struct Router<S = (), B = Body> { /* private fields */ }
Expand description

The router type for composing handlers and services.

Implementations

Create a new Router.

Unless you add additional routes this will respond with 404 Not Found to all requests.

Create a new Router with the given state.

See State for more details about accessing state.

Unless you add additional routes this will respond with 404 Not Found to all requests.

Create a new Router that inherits its state from another Router that it is merged into or nested under.

Example
use axum::{Router, routing::get, extract::State};

#[derive(Clone)]
struct AppState {}

// A router that will be nested under the `app` router.
//
// By using `inherit_state` we'll reuse the state from the `app` router.
let nested_router = Router::inherit_state()
    .route("/bar", get(|state: State<AppState>| async {}));

// A router that will be merged into the `app` router.
let merged_router = Router::inherit_state()
    .route("/baz", get(|state: State<AppState>| async {}));

let app = Router::with_state(AppState {})
    .route("/", get(|state: State<AppState>| async {}))
    .nest("/foo", nested_router)
    .merge(merged_router);

// `app` now has routes for `/`, `/foo/bar`, and `/baz` that all use the same state.

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.

method_router is the MethodRouter that should receive the request if the path matches path. method_router 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. 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

To accept multiple methods for the same route you must add all handlers at the same time:

use axum::{Router, routing::{get, delete}, extract::Path};

let app = Router::new().route(
    "/",
    get(get_root).post(post_root).delete(delete_root),
);

async fn get_root() {}

async fn post_root() {}

async fn delete_root() {}
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>) {}
Panics

Panics if the route overlaps with another route:

use axum::{routing::get, Router};

let app = Router::new()
    .route("/", get(|| async {}))
    .route("/", get(|| async {}));

The static route /foo and the dynamic route /:key are not considered to overlap and /foo will take precedence.

Also panics if path is empty.

Add another route to the router that calls a Service.

Example
use axum::{
    Router,
    body::Body,
    routing::{any_service, get_service},
    http::{Request, StatusCode},
    error_handling::HandleErrorLayer,
};
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 whose response body is not `axum::body::BoxBody`
        // can be wrapped in `axum::routing::any_service` (or one of the other routing filters)
        // to have the response body mapped
        any_service(service_fn(|_: Request<Body>| async {
            let res = Response::new(Body::from("Hi from `GET /`"));
            Ok::<_, Infallible>(res)
        }))
    )
    .route_service(
        "/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::boxed(body);
            let res = Response::new(body);
            Ok::<_, Infallible>(res)
        })
    )
    .route(
        // GET `/static/Cargo.toml` goes to a service from tower-http
        "/static/Cargo.toml",
        get_service(ServeFile::new("Cargo.toml"))
            // though we must handle any potential errors
            .handle_error(|error: io::Error| async move {
                (
                    StatusCode::INTERNAL_SERVER_ERROR,
                    format!("Unhandled internal error: {}", error),
                )
            })
    );

Routing to arbitrary services in this way has complications for backpressure (Service::poll_ready). See the Routing to services and backpressure module for more details.

Panics

Panics for the same reasons as Router::route or if you attempt to route to a Router:

use axum::{routing::get, Router};

let app = Router::new().route_service(
    "/",
    Router::new().route("/foo", get(|| async {})).into_service(),
);

Use Router::nest instead.

Nest a Service at some path.

This allows you to break your application into smaller pieces and compose them together.

Example
use axum::{
    routing::{get, post},
    Router,
};

let user_routes = Router::new().route("/:id", get(|| async {}));

let team_routes = Router::new().route("/", post(|| async {}));

let api_routes = Router::new()
    .nest("/users", user_routes)
    .nest("/teams", team_routes);

let app = Router::new().nest("/api", api_routes);

// Our app now accepts
// - GET /api/users/:id
// - POST /api/teams
How the URI changes

Note that nested routes will not see the original request URI but instead have the matched prefix stripped. This is necessary for services like static file serving to work. Use OriginalUri if you need the original request URI.

Captures from outer routes

Take care when using nest together with dynamic routes as nesting also captures from the outer routes:

use axum::{
    extract::Path,
    routing::get,
    Router,
};
use std::collections::HashMap;

async fn users_get(Path(params): Path<HashMap<String, String>>) {
    // Both `version` and `id` were captured even though `users_api` only
    // explicitly captures `id`.
    let version = params.get("version");
    let id = params.get("id");
}

let users_api = Router::new().route("/users/:id", get(users_get));

let app = Router::new().nest("/:version/api", users_api);
Differences to 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 the prefix stripped:

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("/bar", nested_router);
Fallbacks

When nesting a router, if a request matches the prefix but the nested router doesn’t have a matching route, the outer fallback will not be called:

use axum::{routing::get, http::StatusCode, handler::Handler, Router};

async fn fallback() -> (StatusCode, &'static str) {
    (StatusCode::NOT_FOUND, "Not Found")
}

let api_routes = Router::new().nest_service("/users", get(|| async {}));

let app = Router::new()
    .nest("/api", api_routes)
    .fallback(fallback);

Here requests like GET /api/not-found will go into api_routes and then to the fallback of api_routes which will return an empty 404 Not Found response. The outer fallback declared on app will not be called.

Think of nested services as swallowing requests that matches the prefix and not falling back to outer router even if they don’t have a matching route.

You can still add separate fallbacks to nested routers:

use axum::{routing::get, http::StatusCode, handler::Handler, Json, Router};
use serde_json::{json, Value};

async fn fallback() -> (StatusCode, &'static str) {
    (StatusCode::NOT_FOUND, "Not Found")
}

async fn api_fallback() -> (StatusCode, Json<Value>) {
    (StatusCode::NOT_FOUND, Json(json!({ "error": "Not Found" })))
}

let api_routes = Router::new()
    .nest_service("/users", get(|| async {}))
    // add dedicated fallback for requests starting with `/api`
    .fallback(api_fallback);

let app = Router::new()
    .nest("/api", api_routes)
    .fallback(fallback);
Panics
  • If the route overlaps with another route. See Router::route for more details.
  • If the route contains a wildcard (*).
  • If path is empty.

Like nest, but accepts an arbitrary Service.

Merge two routers into one.

This is useful for breaking apps into smaller pieces and combining them into one.

use axum::{
    routing::get,
    Router,
};

// define some routes separately
let user_routes = Router::new()
    .route("/users", get(users_list))
    .route("/users/:id", get(users_show));

let team_routes = Router::new()
    .route("/teams", get(teams_list));

// combine them into one
let app = Router::new()
    .merge(user_routes)
    .merge(team_routes);

// could also do `user_routes.merge(team_routes)`

// Our app now accepts
// - GET /users
// - GET /users/:id
// - POST /teams
Panics
  • If two routers that each have a fallback are merged. This is because Router only allows a single fallback.

Apply a tower::Layer to all routes in the router.

This can be used to add additional processing to a request for a group of routes.

Note that the middleware is only applied to existing routes. So you have to first add your routes (and / or fallback) and then call layer afterwards. Additional routes added after layer is called will not have the middleware added.

If you want to add middleware to a single handler you can either use MethodRouter::layer or Handler::layer.

Example

Adding the tower_http::trace::TraceLayer:

use axum::{routing::get, Router};
use tower_http::trace::TraceLayer;

let app = Router::new()
    .route("/foo", get(|| async {}))
    .route("/bar", get(|| async {}))
    .layer(TraceLayer::new_for_http());

If you need to write your own middleware see “Writing middleware” for the different options.

If you only want middleware on some routes you can use Router::merge:

use axum::{routing::get, Router};
use tower_http::{trace::TraceLayer, compression::CompressionLayer};

let with_tracing = Router::new()
    .route("/foo", get(|| async {}))
    .layer(TraceLayer::new_for_http());

let with_compression = Router::new()
    .route("/bar", get(|| async {}))
    .layer(CompressionLayer::new());

// Merge everything into one `Router`
let app = Router::new()
    .merge(with_tracing)
    .merge(with_compression);
Multiple middleware

It’s recommended to use tower::ServiceBuilder when applying multiple middleware. See middleware for more details.

Runs after routing

Middleware added with this method will run after routing and thus cannot be used to rewrite the request URI. See “Rewriting request URI in middleware” for more details and a workaround.

Error handling

See middleware for details on how error handling impacts middleware.

Apply a tower::Layer to the router that will only run if the request matches a route.

Note that the middleware is only applied to existing routes. So you have to first add your routes (and / or fallback) and then call layer afterwards. Additional routes added after layer is called will not have the middleware added.

This works similarly to Router::layer except the middleware will only run if the request matches a route. This is useful for middleware that return early (such as authorization) which might otherwise convert a 404 Not Found into a 401 Unauthorized.

Example
use axum::{
    routing::get,
    Router,
};
use tower_http::auth::RequireAuthorizationLayer;

let app = Router::new()
    .route("/foo", get(|| async {}))
    .route_layer(RequireAuthorizationLayer::bearer("password"));

// `GET /foo` with a valid token will receive `200 OK`
// `GET /foo` with a invalid token will receive `401 Unauthorized`
// `GET /not-found` with a invalid token will receive `404 Not Found`

Add a fallback Handler to the router.

This service will be called if no routes matches the incoming request.

use axum::{
    Router,
    routing::get,
    handler::Handler,
    response::IntoResponse,
    http::{StatusCode, Uri},
};

let app = Router::new()
    .route("/foo", get(|| async { /* ... */ }))
    .fallback(fallback);

async fn fallback(uri: Uri) -> (StatusCode, String) {
    (StatusCode::NOT_FOUND, format!("No route for {}", uri))
}

Fallbacks only apply to routes that aren’t matched by anything in the router. If a handler is matched by a request but returns 404 the fallback is not called.

Add a fallback Service to the router.

See Router::fallback for more details.

Convert this router into a RouterService.

Panics

Panics if the router was constructed with Router::inherit_state.

Convert this router into a MakeService, that is a Service whose response is another service.

This is useful when running your application with hyper’s Server:

use axum::{
    routing::get,
    Router,
};

let app = Router::new().route("/", get(|| async { "Hi!" }));

axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
    .serve(app.into_make_service())
    .await
    .expect("server failed");
Available on crate feature tokio only.

Convert this router into a MakeService, that will store C’s associated ConnectInfo in a request extension such that ConnectInfo can extract it.

This enables extracting things like the client’s remote address.

Extracting std::net::SocketAddr is supported out of the box:

use axum::{
    extract::ConnectInfo,
    routing::get,
    Router,
};
use std::net::SocketAddr;

let app = Router::new().route("/", get(handler));

async fn handler(ConnectInfo(addr): ConnectInfo<SocketAddr>) -> String {
    format!("Hello {}", addr)
}

axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
    .serve(
        app.into_make_service_with_connect_info::<SocketAddr>()
    )
    .await
    .expect("server failed");

You can implement custom a Connected like so:

use axum::{
    extract::connect_info::{ConnectInfo, Connected},
    routing::get,
    Router,
};
use hyper::server::conn::AddrStream;

let app = Router::new().route("/", get(handler));

async fn handler(
    ConnectInfo(my_connect_info): ConnectInfo<MyConnectInfo>,
) -> String {
    format!("Hello {:?}", my_connect_info)
}

#[derive(Clone, Debug)]
struct MyConnectInfo {
    // ...
}

impl Connected<&AddrStream> for MyConnectInfo {
    fn connect_info(target: &AddrStream) -> Self {
        MyConnectInfo {
            // ...
        }
    }
}

axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
    .serve(
        app.into_make_service_with_connect_info::<MyConnectInfo>()
    )
    .await
    .expect("server failed");

See the unix domain socket example for an example of how to use this to collect UDS connection info.

Trait Implementations

Returns a copy of the value. Read more
Performs copy-assignment from source. Read more
Formats the value using the given formatter. Read more
Returns the “default value” for a type. Read more

Auto Trait Implementations

Blanket Implementations

Gets the TypeId of self. Read more
Immutably borrows from an owned value. Read more
Mutably borrows from an owned value. Read more

Returns the argument unchanged.

Converts to this type from a reference to the input type.
Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Instruments this type with the current Span, returning an Instrumented wrapper. Read more

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Should always be Self
The resulting type after obtaining ownership.
Creates owned data from borrowed data, usually by cloning. Read more
Uses borrowed data to replace owned data, usually by cloning. Read more
The type returned in the event of a conversion error.
Performs the conversion.
The type returned in the event of a conversion error.
Performs the conversion.
Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more