Expand description
This crate provides convenient and reliable way to work with routes
in axum. Define routes along with their methods, paths and handlers
in an enum, add construct axum::Router with a single call, then refer
to any route (and construct a link to it) via corresponding enum variant,
which provides compile time checked internal links for your application.
Also, axum extractor is provided for handlers to be aware of their routes,
and capable of constructing links to themselves.
§Example
use axum::extract::Path;
use axum_myroutes::routes;
// Specify routes
#[derive(Clone, Copy)]
#[routes]
enum Route {
#[get("/", handler = home)]
Home,
#[get("/items/{id}", handler = item_by_id)]
ItemById,
}
async fn home() -> String {
// Construct links to routes
format!(
"<a href={}>To first item</a>",
Route::ItemById.url_for().path_param("id", 1).unwrap().build().unwrap()
)
}
// My{...} extractor is generated for the enum
async fn item_by_id(route: MyRoute, Path(id): Path<u64>) -> String {
format!(
"<a href={}>To home</a><a href={}>To self</a><a href={}>To next</a>",
Route::Home.url_for().build().unwrap(),
// Construct links to current route, parameters are already filled...
route.url_for().build().unwrap(),
// ...but can be modified
route.url_for().path_param("id", id + 1).unwrap().build().unwrap(),
)
}
#[tokio::main]
async fn main() {
let app = Route::to_router();
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}§Route methods and path construction
#[routes] enum has methods to query information related to route and to construct
paths to it. It’s possible to specify path params with param(), query params with
query_param() and fragment with fragment() methods of PathBuilder returned by
url_for().
// Get route path (pattern)
assert_eq!(Route::ItemById.path(), "/{id}");
// Get route name (name of enum variant)
assert_eq!(Route::ItemById.name(), "ItemById");
// Construct url (path_param fails on unknown parameter)
assert_eq!(Route::ItemById.url_for().path_param("id", 123).unwrap().build().unwrap(), "/123");
// But there's also shorted relaxed variant under `generic_param` feature
// which can sets both path and query params
//assert_eq!(Route::ItemById.url_for().param("id", 123).build().unwrap(), "/123");
// Error on missing parameter
assert!(Route::ItemById.url_for().build().is_err());
// Can also set query params and fragment
assert_eq!(
Route::ItemById
.url_for()
.path_param("id", 123).unwrap()
.query_param("foo", "bar")
.fragment("frag")
.build()
.unwrap(),
"/123?foo=bar#frag"
);§Route props
A type to store additional route properties can be provided, set per-route, and retrieved with route method.
There are options to toggle Default requirement on props type, and to allow
static construction of props, enabled through routes arguments.
#[derive(Default)]
struct RouteProps {
require_auth: bool,
}
#[derive(Clone, Copy)]
#[routes(props_type = RouteProps)]
enum Route {
#[get("/public", handler = handler)]
Public,
#[get("/private", handler = handler, props = RouteProps { require_auth: true })]
Private,
}
assert_eq!(Route::Public.props().require_auth, false);
assert_eq!(Route::Private.props().require_auth, true);§Extractors
Extractor type is automatically provided for the route enum, with the same
name prefixed with My.
async fn item_by_id(route: MyRoute) {
// Same methods as route variant
assert_eq!(route.path(), "/");
assert_eq!(route.name(), "ItemById");
assert_eq!(route.props().require_auth, false);
// In extractor, parameters are already filled,
// so path to self can be constructed right away
assert!(route.url_for().build().is_ok());
}§Router with state
If router with state is used (e.g. .with_state() is called on a router), the
state type must be passed to #[routes] argument:
#[derive(Clone)]
struct AppState;
#[derive(Clone, Copy)]
#[routes(state_type = AppState)] // note state_type argument
enum Route {
#[get("/", handler = handler)]
Home,
}
#[tokio::main]
async fn main() {
let app = Route::to_router()
.with_state(AppState); // add state as usual
}§Middleware with access to current route
Current route can be accessed from a middleware, and this provides a powerful
mechanism to control routes behavior in a centralized way with route props.
However, for a middleware to see a current route, it should be added to a router
before the route information is added, which means you can’t add such middleware
to constructed Router. Instead, use to_router_with() to intervene with the
router construction and insert middleware a the right spot.
use axum::{extract::Request, middleware::{from_fn, Next}, response::IntoResponse};
async fn middleware(route: MyRoute, request: Request, next: Next) -> impl IntoResponse {
if route.props().require_auth {
// check auth
}
next.run(request).await
}
#[tokio::main]
async fn main() {
let app = Route::to_router_with(|route| route.layer(from_fn(middleware)));
}Structs§
- Path
Builder - Route path builder.
Enums§
Attribute Macros§
- routes
- Main attribute macro for routes enum.