Struct tonic::service::AxumRouter
source · pub struct AxumRouter<S = ()> { /* private fields */ }
Expand description
The router type for composing handlers and services.
Implementations§
source§impl<S> Router<S>
impl<S> Router<S>
sourcepub fn new() -> Router<S>
pub fn new() -> Router<S>
Create a new Router
.
Unless you add additional routes this will respond with 404 Not Found
to
all requests.
sourcepub fn route(self, path: &str, method_router: MethodRouter<S>) -> Router<S>
pub fn route(self, path: &str, method_router: MethodRouter<S>) -> Router<S>
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
. 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.
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
Note that /*key
doesn’t match empty segments. Thus:
/*key
doesn’t match/
but does match/a
,/a/
, etc./x/*key
doesn’t match/x
or/x/
but does match/x/a
,/x/a/
, etc.
Wildcard captures can also be extracted using Path
:
use axum::{
Router,
routing::get,
extract::Path,
};
let app: Router = Router::new().route("/*key", get(handler));
async fn handler(Path(path): Path<String>) -> String {
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 can 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() {}
Or you can add them one by one:
let app = Router::new()
.route("/", get(get_root))
.route("/", post(post_root))
.route("/", delete(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.
sourcepub fn route_service<T>(self, path: &str, service: T) -> Router<S>
pub fn route_service<T>(self, path: &str, service: T) -> Router<S>
Add another route to the router that calls a Service
.
§Example
use axum::{
Router,
body::Body,
routing::{any_service, get_service},
extract::Request,
http::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| 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| async move {
let body = Body::from(format!("Hi from `{} /foo`", req.method()));
let res = Response::new(body);
Ok::<_, Infallible>(res)
})
)
.route_service(
// GET `/static/Cargo.toml` goes to a service from tower-http
"/static/Cargo.toml",
ServeFile::new("Cargo.toml"),
);
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 {})),
);
Use Router::nest
instead.
sourcepub fn nest(self, path: &str, router: Router<S>) -> Router<S>
pub fn nest(self, path: &str, router: Router<S>) -> Router<S>
Nest a Router
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 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 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);
Additionally, while the wildcard route /foo/*rest
will not match the
paths /foo
or /foo/
, a nested router at /foo
will 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 axum::{routing::get, http::StatusCode, handler::Handler, Router};
async fn fallback() -> (StatusCode, &'static str) {
(StatusCode::NOT_FOUND, "Not Found")
}
let api_routes = Router::new().route("/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
but 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. the fallback
function.
If the nested router has its own fallback then the outer fallback will not be inherited:
use axum::{
routing::get,
http::StatusCode,
handler::Handler,
Json,
Router,
};
async fn fallback() -> (StatusCode, &'static str) {
(StatusCode::NOT_FOUND, "Not Found")
}
async fn api_fallback() -> (StatusCode, Json<serde_json::Value>) {
(
StatusCode::NOT_FOUND,
Json(serde_json::json!({ "status": "Not Found" })),
)
}
let api_routes = Router::new()
.route("/users", get(|| async {}))
.fallback(api_fallback);
let app = Router::new()
.nest("/api", api_routes)
.fallback(fallback);
Here requests like GET /api/not-found
will go to api_fallback
.
§Nesting routers with state
When combining Router
s with this method, each Router
must have the
same type of state. If your routers have different types you can use
Router::with_state
to provide the state and make the types match:
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("/foo", inner_router)
.with_state(OuterState {});
Note that the inner router will still inherit the fallback from the outer router.
§Panics
- If the route overlaps with another route. See
Router::route
for more details. - If the route contains a wildcard (
*
). - If
path
is empty.
sourcepub fn nest_service<T>(self, path: &str, service: T) -> Router<S>
pub fn nest_service<T>(self, path: &str, service: T) -> Router<S>
Like nest
, but accepts an arbitrary Service
.
sourcepub fn merge<R>(self, other: R) -> Router<S>
pub fn merge<R>(self, other: R) -> 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 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
// - GET /teams
§Merging routers with state
When combining Router
s with this method, each Router
must have the
same type of state. If your routers have different types you can use
Router::with_state
to provide the state and make the types match:
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))
.merge(inner_router)
.with_state(OuterState {});
§Merging routers with fallbacks
When combining Router
s 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
Router
only allows a single fallback.
sourcepub fn layer<L>(self, layer: L) -> Router<S>where
L: Layer<Route> + Clone + Send + 'static,
<L as Layer<Route>>::Service: Service<Request<Body>> + Clone + Send + 'static,
<<L as Layer<Route>>::Service as Service<Request<Body>>>::Response: IntoResponse + 'static,
<<L as Layer<Route>>::Service as Service<Request<Body>>>::Error: Into<Infallible> + 'static,
<<L as Layer<Route>>::Service as Service<Request<Body>>>::Future: Send + 'static,
pub fn layer<L>(self, layer: L) -> Router<S>where
L: Layer<Route> + Clone + Send + 'static,
<L as Layer<Route>>::Service: Service<Request<Body>> + Clone + Send + 'static,
<<L as Layer<Route>>::Service as Service<Request<Body>>>::Response: IntoResponse + 'static,
<<L as Layer<Route>>::Service as Service<Request<Body>>>::Error: Into<Infallible> + 'static,
<<L as Layer<Route>>::Service as Service<Request<Body>>>::Future: Send + 'static,
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.
sourcepub fn route_layer<L>(self, layer: L) -> Router<S>where
L: Layer<Route> + Clone + Send + 'static,
<L as Layer<Route>>::Service: Service<Request<Body>> + Clone + Send + 'static,
<<L as Layer<Route>>::Service as Service<Request<Body>>>::Response: IntoResponse + 'static,
<<L as Layer<Route>>::Service as Service<Request<Body>>>::Error: Into<Infallible> + 'static,
<<L as Layer<Route>>::Service as Service<Request<Body>>>::Future: Send + 'static,
pub fn route_layer<L>(self, layer: L) -> Router<S>where
L: Layer<Route> + Clone + Send + 'static,
<L as Layer<Route>>::Service: Service<Request<Body>> + Clone + Send + 'static,
<<L as Layer<Route>>::Service as Service<Request<Body>>>::Response: IntoResponse + 'static,
<<L as Layer<Route>>::Service as Service<Request<Body>>>::Error: Into<Infallible> + 'static,
<<L as Layer<Route>>::Service as Service<Request<Body>>>::Future: Send + 'static,
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 route_layer
afterwards. Additional routes added after route_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
.
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 axum::{
routing::get,
Router,
};
use tower_http::validate_request::ValidateRequestHeaderLayer;
let app = Router::new()
.route("/foo", get(|| async {}))
.route_layer(ValidateRequestHeaderLayer::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`
sourcepub fn has_routes(&self) -> bool
pub fn has_routes(&self) -> bool
True if the router currently has at least one route added.
sourcepub fn fallback<H, T>(self, handler: H) -> Router<S>where
H: Handler<T, S>,
T: 'static,
pub fn fallback<H, T>(self, handler: H) -> Router<S>where
H: Handler<T, S>,
T: 'static,
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. Note that this applies to MethodRouter
s too: if the
request hits a valid path but the MethodRouter
does not have an appropriate
method handler installed, the fallback is not called (use
MethodRouter::fallback
for 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 axum::Router;
async fn handler() {}
let app = Router::new().fallback(handler);
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
Running the handler directly is faster since it avoids the overhead of routing:
use axum::handler::HandlerWithoutStateExt;
async fn handler() {}
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, handler.into_make_service()).await.unwrap();
sourcepub fn fallback_service<T>(self, service: T) -> Router<S>
pub fn fallback_service<T>(self, service: T) -> Router<S>
Add a fallback Service
to the router.
See Router::fallback
for more details.
sourcepub fn method_not_allowed_fallback<H, T>(self, handler: H) -> Router<S>where
H: Handler<T, S>,
T: 'static,
pub fn method_not_allowed_fallback<H, T>(self, handler: H) -> Router<S>where
H: Handler<T, S>,
T: 'static,
Add a fallback Handler
for the case where a route exists, but the method of the request is not supported.
Sets a fallback on all previously registered MethodRouter
s,
to be called when no matching method handler is set.
use axum::{response::IntoResponse, routing::get, Router};
async fn hello_world() -> impl IntoResponse {
"Hello, world!\n"
}
async fn default_fallback() -> impl IntoResponse {
"Default fallback\n"
}
async fn handle_405() -> impl IntoResponse {
"Method not allowed fallback"
}
#[tokio::main]
async fn main() {
let router = Router::new()
.route("/", get(hello_world))
.fallback(default_fallback)
.method_not_allowed_fallback(handle_405);
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, router).await.unwrap();
}
The fallback only applies if there is a MethodRouter
registered for a given path,
but the method used in the request is not specified. In the example, a GET
on
http://localhost:3000
causes the hello_world
handler to react, while issuing a
POST
triggers handle_405
. Calling an entirely different route, like http://localhost:3000/hello
causes default_fallback
to run.
sourcepub fn with_state<S2>(self, state: S) -> Router<S2>
pub fn with_state<S2>(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 Extension
instead for such data.
use axum::{Router, routing::get, extract::State};
#[derive(Clone)]
struct AppState {}
let routes = Router::new()
.route("/", get(|State(state): State<AppState>| async {
// use state
}))
.with_state(AppState {});
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, routes).await.unwrap();
§Returning routers with states from functions
When returning Router
s from functions, it is generally recommended not to set the
state directly:
use axum::{Router, routing::get, extract::State};
#[derive(Clone)]
struct AppState {}
// Don't call `Router::with_state` here
fn routes() -> Router<AppState> {
Router::new()
.route("/", get(|_: State<AppState>| async {}))
}
// Instead do it before you run the server
let routes = routes().with_state(AppState {});
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, routes).await.unwrap();
If you do need to provide the state, and you’re not nesting/merging the router
into another router, then return Router
without any type parameters:
// Don't return `Router<AppState>`
fn routes(state: AppState) -> Router {
Router::new()
.route("/", get(|_: State<AppState>| async {}))
.with_state(state)
}
let routes = routes(AppState {});
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, routes).await.unwrap();
This is because we can only call Router::into_make_service
on Router<()>
,
not Router<AppState>
. See below for more details about why that is.
Note that the state defaults to ()
so Router
and Router<()>
is the same.
If you are nesting/merging the router it is recommended to use a generic state type on the resulting router:
fn routes<S>(state: AppState) -> Router<S> {
Router::new()
.route("/", get(|_: State<AppState>| async {}))
.with_state(state)
}
let routes = Router::new().nest("/api", routes(AppState {}));
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, routes).await.unwrap();
§What S
in Router<S>
means
Router<S>
means a router that is missing a state of type S
to be able to
handle requests. It does not mean a Router
that has a state of type S
.
For example:
// A router that _needs_ an `AppState` to handle requests
let router: Router<AppState> = Router::new()
.route("/", get(|_: State<AppState>| async {}));
// 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<()> = router.with_state(AppState {});
// 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`.
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, router).await.unwrap();
Perhaps a little counter intuitively, Router::with_state
doesn’t always return a
Router<()>
. Instead you get to pick what the new missing state type is:
let router: Router<AppState> = Router::new()
.route("/", get(|_: State<AppState>| async {}));
// 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<String> = router.with_state(AppState {});
// That allows us to add new routes that uses `String` as the state type
let string_router = string_router
.route("/needs-string", get(|_: State<String>| async {}));
// Provide the `String` and choose `()` as the new missing state.
let final_router: Router<()> = string_router.with_state("foo".to_owned());
// Since we have a `Router<()>` we can run it.
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, final_router).await.unwrap();
This why this returning Router<AppState>
after calling with_state
doesn’t
work:
// This won't work because we're returning a `Router<AppState>`
// i.e. we're saying we're still missing an `AppState`
fn routes(state: AppState) -> Router<AppState> {
Router::new()
.route("/", get(|_: State<AppState>| async {}))
.with_state(state)
}
let app = routes(AppState {});
// We can only call `Router::into_make_service` on a `Router<()>`
// but `app` is a `Router<AppState>`
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
Instead return Router<()>
since we have provided all the state needed:
// We've provided all the state necessary so return `Router<()>`
fn routes(state: AppState) -> Router<()> {
Router::new()
.route("/", get(|_: State<AppState>| async {}))
.with_state(state)
}
let app = routes(AppState {});
// We can now call `Router::into_make_service`
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
§A note about performance
If you need a Router
that implements Service
but 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 axum::{Router, routing::get};
let app = Router::new()
.route("/", get(|| async { /* ... */ }))
// even though we don't need any state, call `with_state(())` anyway
.with_state(());
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_service
and [Router::into_make_service_with_connect_info
]
do this automatically.
sourcepub fn as_service<B>(&mut self) -> RouterAsService<'_, B, S>
pub fn as_service<B>(&mut self) -> RouterAsService<'_, B, S>
Convert the router into a borrowed Service
with a fixed request body type, to aid type
inference.
In some cases when calling methods from tower::ServiceExt
on a Router
you might get
type inference errors along the lines of
let response = router.ready().await?.call(request).await?;
^^^^^ cannot infer type for type parameter `B`
This happens because Router
implements Service
with impl<B> Service<Request<B>> for Router<()>
.
For example:
use axum::{
Router,
routing::get,
http::Request,
body::Body,
};
use tower::{Service, ServiceExt};
let mut router = Router::new().route("/", get(|| async {}));
let request = Request::new(Body::empty());
let response = router.ready().await?.call(request).await?;
Calling Router::as_service
fixes that:
use axum::{
Router,
routing::get,
http::Request,
body::Body,
};
use tower::{Service, ServiceExt};
let mut router = Router::new().route("/", get(|| async {}));
let request = Request::new(Body::empty());
let response = router.as_service().ready().await?.call(request).await?;
This is mainly used when calling Router
in tests. It shouldn’t be necessary when running
the Router
normally via Router::into_make_service
.
sourcepub fn into_service<B>(self) -> RouterIntoService<B, S>
pub fn into_service<B>(self) -> RouterIntoService<B, S>
Convert the router into an owned Service
with a fixed request body type, to aid type
inference.
This is the same as Router::as_service
instead it returns an owned Service
. See
that method for more details.
source§impl Router
impl Router
sourcepub fn into_make_service(self) -> IntoMakeService<Router>
pub fn into_make_service(self) -> IntoMakeService<Router>
Convert this router into a MakeService
, that is a Service
whose
response is another service.
use axum::{
routing::get,
Router,
};
let app = Router::new().route("/", get(|| async { "Hi!" }));
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
Trait Implementations§
source§impl<B> Service<Request<B>> for Router
impl<B> Service<Request<B>> for Router
source§type Error = Infallible
type Error = Infallible
source§type Future = RouteFuture<Infallible>
type Future = RouteFuture<Infallible>
Auto Trait Implementations§
impl<S> Freeze for Router<S>
impl<S = ()> !RefUnwindSafe for Router<S>
impl<S> Send for Router<S>
impl<S> Sync for Router<S>
impl<S> Unpin for Router<S>
impl<S = ()> !UnwindSafe for Router<S>
Blanket Implementations§
source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
source§impl<T, ReqBody, ResBody> GrpcService<ReqBody> for T
impl<T, ReqBody, ResBody> GrpcService<ReqBody> for T
source§type ResponseBody = ResBody
type ResponseBody = ResBody
source§fn poll_ready(
&mut self,
cx: &mut Context<'_>,
) -> Poll<Result<(), <T as GrpcService<ReqBody>>::Error>>
fn poll_ready( &mut self, cx: &mut Context<'_>, ) -> Poll<Result<(), <T as GrpcService<ReqBody>>::Error>>
Ready
when the service is able to process requests. Read moresource§impl<T> Instrument for T
impl<T> Instrument for T
source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
source§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T
in a tonic::Request
source§impl<S, R> ServiceExt<R> for Swhere
S: Service<R>,
impl<S, R> ServiceExt<R> for Swhere
S: Service<R>,
source§fn into_make_service(self) -> IntoMakeService<S>
fn into_make_service(self) -> IntoMakeService<S>
MakeService
, that is a Service
whose
response is another service. Read moresource§fn handle_error<F, T>(self, f: F) -> HandleError<Self, F, T>
fn handle_error<F, T>(self, f: F) -> HandleError<Self, F, T>
HandleError
, that will handle errors
by converting them into responses. Read moresource§impl<T, Request> ServiceExt<Request> for T
impl<T, Request> ServiceExt<Request> for T
source§fn ready(&mut self) -> Ready<'_, Self, Request> ⓘwhere
Self: Sized,
fn ready(&mut self) -> Ready<'_, Self, Request> ⓘwhere
Self: Sized,
source§fn ready_and(&mut self) -> Ready<'_, Self, Request> ⓘwhere
Self: Sized,
fn ready_and(&mut self) -> Ready<'_, Self, Request> ⓘwhere
Self: Sized,
ServiceExt::ready
method insteadsource§fn ready_oneshot(self) -> ReadyOneshot<Self, Request> ⓘwhere
Self: Sized,
fn ready_oneshot(self) -> ReadyOneshot<Self, Request> ⓘwhere
Self: Sized,
source§fn oneshot(self, req: Request) -> Oneshot<Self, Request> ⓘwhere
Self: Sized,
fn oneshot(self, req: Request) -> Oneshot<Self, Request> ⓘwhere
Self: Sized,
Service
, calling with the providing request once it is ready.source§fn and_then<F>(self, f: F) -> AndThen<Self, F>
fn and_then<F>(self, f: F) -> AndThen<Self, F>
poll_ready
method. Read moresource§fn map_response<F, Response>(self, f: F) -> MapResponse<Self, F>
fn map_response<F, Response>(self, f: F) -> MapResponse<Self, F>
poll_ready
method. Read moresource§fn map_err<F, Error>(self, f: F) -> MapErr<Self, F>
fn map_err<F, Error>(self, f: F) -> MapErr<Self, F>
poll_ready
method. Read moresource§fn map_result<F, Response, Error>(self, f: F) -> MapResult<Self, F>
fn map_result<F, Response, Error>(self, f: F) -> MapResult<Self, F>
Result<Self::Response, Self::Error>
)
to a different value, regardless of whether the future succeeds or
fails. Read moresource§fn map_request<F, NewRequest>(self, f: F) -> MapRequest<Self, F>
fn map_request<F, NewRequest>(self, f: F) -> MapRequest<Self, F>
source§fn then<F, Response, Error, Fut>(self, f: F) -> Then<Self, F>
fn then<F, Response, Error, Fut>(self, f: F) -> Then<Self, F>
source§fn map_future<F, Fut, Response, Error>(self, f: F) -> MapFuture<Self, F>
fn map_future<F, Fut, Response, Error>(self, f: F) -> MapFuture<Self, F>
source§impl<T, Request> ServiceExt<Request> for T
impl<T, Request> ServiceExt<Request> for T
source§fn ready(&mut self) -> Ready<'_, Self, Request> ⓘwhere
Self: Sized,
fn ready(&mut self) -> Ready<'_, Self, Request> ⓘwhere
Self: Sized,
source§fn ready_oneshot(self) -> ReadyOneshot<Self, Request> ⓘwhere
Self: Sized,
fn ready_oneshot(self) -> ReadyOneshot<Self, Request> ⓘwhere
Self: Sized,
source§fn oneshot(self, req: Request) -> Oneshot<Self, Request> ⓘwhere
Self: Sized,
fn oneshot(self, req: Request) -> Oneshot<Self, Request> ⓘwhere
Self: Sized,
Service
, calling it with the provided request once it is ready.source§fn and_then<F>(self, f: F) -> AndThen<Self, F>
fn and_then<F>(self, f: F) -> AndThen<Self, F>
poll_ready
method. Read moresource§fn map_response<F, Response>(self, f: F) -> MapResponse<Self, F>
fn map_response<F, Response>(self, f: F) -> MapResponse<Self, F>
poll_ready
method. Read moresource§fn map_err<F, Error>(self, f: F) -> MapErr<Self, F>
fn map_err<F, Error>(self, f: F) -> MapErr<Self, F>
poll_ready
method. Read moresource§fn map_result<F, Response, Error>(self, f: F) -> MapResult<Self, F>
fn map_result<F, Response, Error>(self, f: F) -> MapResult<Self, F>
Result<Self::Response, Self::Error>
)
to a different value, regardless of whether the future succeeds or
fails. Read more