Struct Router
struct Router<S = ()> { ... }
The router type for composing handlers and services.
Router<S> means a router that is missing a state of type S to be able
to handle requests. Thus, only Router<()> (i.e. without missing state) can
be passed to serve. See Router::with_state for more details.
Implementations
impl Router
fn into_make_service(self: Self) -> IntoMakeService<Self>Convert this router into a
MakeService, that is aServicewhose response is another service.use ; let app = new.route; # async ;fn into_make_service_with_connect_info<C>(self: Self) -> IntoMakeServiceWithConnectInfo<Self, C>Convert this router into a
MakeService, that will storeC's associatedConnectInfoin a request extension such thatConnectInfocan extract it.This enables extracting things like the client's remote address.
Extracting
std::net::SocketAddris supported out of the box:use ; use SocketAddr; let app = new.route; async # async ;You can implement custom a
Connectedlike so:use ; use TcpListener; let app = new.route; async # async ;See the unix domain socket example for an example of how to use this to collect UDS connection info.
impl<S> Router<S>
fn new() -> SelfCreate a new
Router.Unless you add additional routes this will respond with
404 Not Foundto all requests.fn without_v07_checks(self: Self) -> SelfTurn off checks for compatibility with route matching syntax from 0.7.
This allows usage of paths starting with a colon
:or an asterisk*which are otherwise prohibited.Example
use ; let app = new .without_v07_checks .route .route; // Our app now accepts // - GET /:colon // - GET /*asterisk # let _: Router = app;Adding such routes without calling this method first will panic.
use ; // This panics... let app = new .route;Merging
When two routers are merged, v0.7 checks are disabled for route registrations on the resulting router if both of the two routers had them also disabled.
Nesting
Each router needs to have the checks explicitly disabled. Nesting a router with the checks either enabled or disabled has no effect on the outer router.
fn route(self: Self, path: &str, method_router: MethodRouter<S>) -> SelfAdd another route to the router.
pathis a string of path segments separated by/. Each segment can be either static, a capture, or a wildcard.method_routeris theMethodRouterthat should receive the request if the path matchespath. Usually,method_routerwill be a handler wrapped in a method router likeget. Seehandlerfor 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 atkey. The value captured can be zero-length except for in the invalid path//.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.
MatchedPathcan 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 atkey.Examples:
/{*key}/assets/{*path}/{id}/{repo}/{*tree}
Note that
/{*key}doesn't match empty segments. Thus:/{*key}doesn't match/but does match/a,/a/, etc./x/{*key}doesn't match/xor/x/but does match/x/a,/x/a/, etc.
Wildcard captures can also be extracted using
Path:use ; let app: Router = new.route; asyncNote that the leading slash is not included, i.e. for the route
/foo/{*rest}and the path/foo/bar/bazthe value ofrestwill bebar/baz.Accepting multiple methods
To accept multiple methods for the same route you can add all handlers at the same time:
use ; let app = new.route; async async async # let _: Router = app;Or you can add them one by one:
# use Router; # use ; # let app = new .route .route .route; # # let _: Router = app; # async # async # asyncMore examples
use ; let app = new .route .route .route .route .route; async async async async async : ) async # let _: Router = app;Panics
Panics if the route overlaps with another route:
use ; let app = new .route .route; # let _: Router = app;The static route
/fooand the dynamic route/{key}are not considered to overlap and/foowill take precedence.Also panics if
pathis empty.fn route_service<T>(self: Self, path: &str, service: T) -> Self where T: Service<Request, Error = Infallible> + Clone + Send + Sync + 'static, <T as >::Response: IntoResponse, <T as >::Future: Send + 'staticAdd another route to the router that calls a
Service.Example
use ; use ServeFile; use Response; use ; use service_fn; let app = new .route .route_service .route_service; # let _: Router = app;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::routeor if you attempt to route to aRouter:use ; let app = new.route_service; # let _: Router = app;Use
Router::nestinstead.fn nest(self: Self, path: &str, router: Router<S>) -> SelfNest a
Routerat some path.This allows you to break your application into smaller pieces and compose them together.
Example
use ; let user_routes = new.route; let team_routes = new.route; let api_routes = new .nest .nest; let app = new.nest; // Our app now accepts // - GET /api/users/{id} // - POST /api/teams # let _: Router = app;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
OriginalUriif you need the original request URI.Captures from outer routes
Take care when using
nesttogether with dynamic routes as nesting also captures from the outer routes:use ; use HashMap; async let users_api = new.route; let app = new.nest; # let _: Router = app;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 the prefix stripped:
use ; let nested_router = new .route; let app = new .route .nest; # let _: Router = app;Additionally, while the wildcard route
/foo/*restwill not match the paths/fooor/foo/, a nested router at/foowill match the path/foo(but not/foo/), and a nested router at/foo/will match the path/foo/(but not/foo).Fallbacks
If a nested router doesn't have its own fallback then it will inherit the fallback from the outer router:
use ; async let api_routes = new.route; let app = new .nest .fallback; # let _: Router = app;Here requests like
GET /api/not-foundwill go intoapi_routesbut because it doesn't have a matching route and doesn't have its own fallback it will call the fallback from the outer router, i.e. thefallbackfunction.If the nested router has its own fallback then the outer fallback will not be inherited:
use ; async async let api_routes = new .route .fallback; let app = new .nest .fallback; # let _: Router = app;Here requests like
GET /api/not-foundwill go toapi_fallback.Nesting routers with state
When combining
Routers with this method, eachRoutermust have the same type of state. If your routers have different types you can useRouter::with_stateto provide the state and make the types match:use ; async let inner_router = new .route .with_state; async let app = new .route .nest .with_state; # let _: Router = app;Note that the inner router will still inherit the fallback from the outer router.
Panics
- If the route overlaps with another route. See
Router::routefor more details. - If the route contains a wildcard (
*). - If
pathis empty.
- If the route overlaps with another route. See
fn nest_service<T>(self: Self, path: &str, service: T) -> Self where T: Service<Request, Error = Infallible> + Clone + Send + Sync + 'static, <T as >::Response: IntoResponse, <T as >::Future: Send + 'staticLike
nest, but accepts an arbitraryService.fn merge<R>(self: Self, other: R) -> Self where R: Into<Router<S>>Merge the paths and fallbacks of two routers into a single
Router.This is useful for breaking apps into smaller pieces and combining them into one.
use ; # # async # async # async // define some routes separately let user_routes = new .route .route; let team_routes = new .route; // combine them into one let app = new .merge .merge; // could also do `user_routes.merge(team_routes)` // Our app now accepts // - GET /users // - GET /users/{id} // - GET /teams # let _: Router = app;Merging routers with state
When combining
Routers with this method, eachRoutermust have the same type of state. If your routers have different types you can useRouter::with_stateto provide the state and make the types match:use ; async let inner_router = new .route .with_state; async let app = new .route .merge .with_state; # let _: Router = app;Merging routers with fallbacks
When combining
Routers with this method, the fallback is also merged. However only one of the routers can have a fallback.Panics
- If two routers that each have a fallback are merged. This
is because
Routeronly allows a single fallback.
- If two routers that each have a fallback are merged. This
is because
fn layer<L>(self: Self, layer: L) -> Router<S> where L: Layer<Route> + Clone + Send + Sync + 'static, <L as >::Service: Service<Request> + Clone + Send + Sync + 'static, <<L as >::Service as Service<Request>>::Response: IntoResponse + 'static, <<L as >::Service as Service<Request>>::Error: Into<Infallible> + 'static, <<L as >::Service as Service<Request>>::Future: Send + 'staticApply a
tower::Layerto 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
layerafterwards. Additional routes added afterlayeris called will not have the middleware added.If you want to add middleware to a single handler you can either use
MethodRouter::layerorHandler::layer.Example
Adding the [
tower_http::trace::TraceLayer]:use ; use TraceLayer; let app = new .route .route .layer; # let _: Router = app;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 ; use ; let with_tracing = new .route .layer; let with_compression = new .route .layer; // Merge everything into one `Router` let app = new .merge .merge; # let _: Router = app;Multiple middleware
It's recommended to use
tower::ServiceBuilderwhen applying multiple middleware. Seemiddlewarefor 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
middlewarefor details on how error handling impacts middleware.fn route_layer<L>(self: Self, layer: L) -> Self where L: Layer<Route> + Clone + Send + Sync + 'static, <L as >::Service: Service<Request> + Clone + Send + Sync + 'static, <<L as >::Service as Service<Request>>::Response: IntoResponse + 'static, <<L as >::Service as Service<Request>>::Error: Into<Infallible> + 'static, <<L as >::Service as Service<Request>>::Future: Send + 'staticApply a
tower::Layerto 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
route_layerafterwards. Additional routes added afterroute_layeris called will not have the middleware added.This works similarly to
Router::layerexcept 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 a404 Not Foundinto a401 Unauthorized.This function will panic if no routes have been declared yet on the router, since the new layer will have no effect, and this is typically a bug. In generic code, you can test if that is the case first, by calling
Router::has_routes.Example
use ; use ValidateRequestHeaderLayer; let app = new .route .route_layer; // `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` # let _: Router = app;fn has_routes(self: &Self) -> boolTrue if the router currently has at least one route added.
fn fallback<H, T>(self: Self, handler: H) -> Self where H: Handler<T, S>, T: 'staticAdd a fallback
Handlerto the router.This service will be called if no routes matches the incoming request.
use ; let app = new .route .fallback; async # let _: Router = app;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. Note that this applies to
MethodRouters too: if the request hits a valid path but theMethodRouterdoes not have an appropriate method handler installed, the fallback is not called (useMethodRouter::fallbackfor this purpose instead).Handling all requests without other routes
Using
Router::new().fallback(...)to accept all request regardless of path or method, if you don't have other routes, isn't optimal:use Router; async let app = new.fallback; # async ;Running the handler directly is faster since it avoids the overhead of routing:
use HandlerWithoutStateExt; async # async ;fn fallback_service<T>(self: Self, service: T) -> Self where T: Service<Request, Error = Infallible> + Clone + Send + Sync + 'static, <T as >::Response: IntoResponse, <T as >::Future: Send + 'staticAdd a fallback
Serviceto the router.See
Router::fallbackfor more details.fn method_not_allowed_fallback<H, T>(self: Self, handler: H) -> Self where H: Handler<T, S>, T: 'staticAdd a fallback
Handlerfor the case where a route exists, but the method of the request is not supported.Sets a fallback on all previously registered
MethodRouters, to be called when no matching method handler is set.use ; async async async asyncThe fallback only applies if there is a
MethodRouterregistered for a given path, but the method used in the request is not specified. In the example, aGETonhttp://localhost:3000causes thehello_worldhandler to react, while issuing aPOSTtriggershandle_405. Calling an entirely different route, likehttp://localhost:3000/hellocausesdefault_fallbackto run.fn reset_fallback(self: Self) -> SelfReset the fallback to its default.
Useful to merge two routers with fallbacks, as
mergedoesn't allow both routers to have an explicit fallback. Use this method to remove the one you want to discard before merging.fn with_state<S2>(self: Self, state: S) -> Router<S2>Provide the state for the router. State passed to this method is global and will be used for all requests this router receives. That means it is not suitable for holding state derived from a request, such as authorization data extracted in a middleware. Use
Extensioninstead for such data.use ; let routes = new .route .with_state; # async ;Returning routers with states from functions
When returning
Routers from functions, it is generally recommended not to set the state directly:use ; // Don't call `Router::with_state` here // Instead do it before you run the server let routes = routes.with_state; # async ;If you do need to provide the state, and you're not nesting/merging the router into another router, then return
Routerwithout any type parameters:# use ; # # # // Don't return `Router<AppState>` let routes = routes; # async ;This is because we can only call
Router::into_make_serviceonRouter<()>, notRouter<AppState>. See below for more details about why that is.Note that the state defaults to
()soRouterandRouter<()>is the same.If you are nesting/merging the router it is recommended to use a generic state type on the resulting router:
# use ; # # # let routes = new.nest; # async ;What
SinRouter<S>meansRouter<S>means a router that is missing a state of typeSto be able to handle requests. It does not mean aRouterthat has a state of typeS.For example:
# use ; # # # // A router that _needs_ an `AppState` to handle requests let router: = new .route; // Once we call `Router::with_state` the router isn't missing // the state anymore, because we just provided it // // Therefore the router type becomes `Router<()>`, i.e a router // that is not missing any state let router: = router.with_state; // Only `Router<()>` has the `into_make_service` method. // // You cannot call `into_make_service` on a `Router<AppState>` // because it is still missing an `AppState`. # async ;Perhaps a little counter intuitively,
Router::with_statedoesn't always return aRouter<()>. Instead you get to pick what the new missing state type is:# use ; # # # let router: = new .route; // When we call `with_state` we're able to pick what the next missing state type is. // Here we pick `String`. let string_router: = router.with_state; // That allows us to add new routes that uses `String` as the state type let string_router = string_router .route; // Provide the `String` and choose `()` as the new missing state. let final_router: = string_router.with_state; // Since we have a `Router<()>` we can run it. # async ;This why this returning
Router<AppState>after callingwith_statedoesn't work:# use ; # # # // This won't work because we're returning a `Router<AppState>` // i.e. we're saying we're still missing an `AppState` let app = routes; // We can only call `Router::into_make_service` on a `Router<()>` // but `app` is a `Router<AppState>` # async ;Instead return
Router<()>since we have provided all the state needed:# use ; # # # // We've provided all the state necessary so return `Router<()>` let app = routes; // We can now call `Router::into_make_service` # async ;A note about performance
If you need a
Routerthat implementsServicebut you don't need any state (perhaps you're making a library that uses axum internally) then it is recommended to call this method before you start serving requests:use ; let app = new .route // even though we don't need any state, call `with_state(())` anyway .with_state; # let _: Router = app;This is not required but it gives axum a chance to update some internals in the router which may impact performance and reduce allocations.
Note that
Router::into_make_serviceandRouter::into_make_service_with_connect_infodo this automatically.fn as_service<B>(self: &mut Self) -> RouterAsService<'_, B, S>Convert the router into a borrowed
Servicewith a fixed request body type, to aid type inference.In some cases when calling methods from
tower::ServiceExton aRouteryou might get type inference errors along the lines oflet response = router.ready().await?.call(request).await?; ^^^^^ cannot infer type for type parameter `B`This happens because
RouterimplementsServicewithimpl<B> Service<Request<B>> for Router<()>.For example:
use axum::{ Router, routing::get, http::Request, body::Body, }; use tower::{Service, ServiceExt}; # async fn async_main() -> Result<(), Box<dyn std::error::Error>> { let mut router = Router::new().route("/", get(|| async {})); let request = Request::new(Body::empty()); let response = router.ready().await?.call(request).await?; # Ok(()) # }Calling
Router::as_servicefixes that:use ; use ; # asyncThis is mainly used when calling
Routerin tests. It shouldn't be necessary when running theRouternormally viaRouter::into_make_service.fn into_service<B>(self: Self) -> RouterIntoService<B, S>Convert the router into an owned
Servicewith a fixed request body type, to aid type inference.This is the same as
Router::as_serviceinstead it returns an ownedService. See that method for more details.
impl<B> Service for Router<()>
fn poll_ready(self: &mut Self, _: &mut Context<'_>) -> Poll<Result<(), <Self as >::Error>>fn call(self: &mut Self, req: Request<B>) -> <Self as >::Future
impl<L> Service for Router<()>
fn poll_ready(self: &mut Self, _cx: &mut Context<'_>) -> Poll<Result<(), <Self as >::Error>>fn call(self: &mut Self, _req: serve::IncomingStream<'_, L>) -> <Self as >::Future
impl<M, S, Target, Request> MakeService for Router<S>
fn poll_ready(self: &mut Self, cx: &mut Context<'_>) -> Poll<Result<(), <M as MakeService<Target, Request>>::MakeError>>fn make_service(self: &mut Self, target: Target) -> <M as MakeService<Target, Request>>::Future
impl<S = ()> RefUnwindSafe for Router<S>
impl<S = ()> UnwindSafe for Router<S>
impl<S> Clone for Router<S>
fn clone(self: &Self) -> Self
impl<S> Debug for Router<S>
fn fmt(self: &Self, f: &mut fmt::Formatter<'_>) -> fmt::Result
impl<S> Default for Router<S>
fn default() -> Self
impl<S> Freeze for Router<S>
impl<S> Send for Router<S>
impl<S> Sync for Router<S>
impl<S> Unpin for Router<S>
impl<S, R> ServiceExt for Router<S>
fn into_make_service(self: Self) -> IntoMakeService<S>fn into_make_service_with_connect_info<C>(self: Self) -> IntoMakeServiceWithConnectInfo<S, C>
impl<T> Any for Router<S>
fn type_id(self: &Self) -> TypeId
impl<T> Borrow for Router<S>
fn borrow(self: &Self) -> &T
impl<T> BorrowMut for Router<S>
fn borrow_mut(self: &mut Self) -> &mut T
impl<T> CloneToUninit for Router<S>
unsafe fn clone_to_uninit(self: &Self, dest: *mut u8)
impl<T> From for Router<S>
fn from(t: T) -> TReturns the argument unchanged.
impl<T> FromRef for Router<S>
fn from_ref(input: &T) -> T
impl<T> Instrument for Router<S>
impl<T> Same for Router<S>
impl<T> ToOwned for Router<S>
fn to_owned(self: &Self) -> Tfn clone_into(self: &Self, target: &mut T)
impl<T> WithSubscriber for Router<S>
impl<T, Request> ServiceExt for Router<S>
impl<T, U> Into for Router<S>
fn into(self: Self) -> UCalls
U::from(self).That is, this conversion is whatever the implementation of
[From]<T> for Uchooses to do.
impl<T, U> TryFrom for Router<S>
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
impl<T, U> TryInto for Router<S>
fn try_into(self: Self) -> Result<U, <U as TryFrom<T>>::Error>
impl<V, T> VZip for Router<S>
fn vzip(self: Self) -> V