Routing
-
breaking: Nested
Router
s will no longer delegate to the outerRouter
's
fallback. Instead you must explicitly set a fallback on the innerRouter
(#1086)This nested router on 0.5:
use axum::{Router, handler::Handler}; let api_routes = Router::new(); let app = Router::new() .nest("/api", api_routes) .fallback(fallback.into_service()); async fn fallback() {}
Becomes this in 0.6:
use axum::Router; let api_routes = Router::new() // we have to explicitly set the fallback here // since nested routers no longer delegate to the outer // router's fallback .fallback(fallback); let app = Router::new() .nest("/api", api_routes) .fallback(fallback); async fn fallback() {}
-
breaking: The request
/foo/
no longer matches/foo/*rest
. If you want
to match/foo/
you have to add a route specifically for that (#1086)For example:
use axum::{Router, routing::get, extract::Path}; let app = Router::new() // this will match `/foo/bar/baz` .route("/foo/*rest", get(handler)) // this will match `/foo/` .route("/foo/", get(handler)) // if you want `/foo` to match you must also add an explicit route for it .route("/foo", get(handler)); async fn handler( // use an `Option` because `/foo/` and `/foo` don't have any path params params: Option<Path<String>>, ) {}
-
breaking: Path params for wildcard routes no longer include the prefix
/
. e.g./foo.js
will match/*filepath
with a value offoo.js
, not
/foo.js
(#1086)For example:
use axum::{Router, routing::get, extract::Path}; let app = Router::new().route("/foo/*rest", get(handler)); async fn handler( Path(params): Path<String>, ) { // for the request `/foo/bar/baz` the value of `params` will be `bar/baz` // // on 0.5 it would be `/bar/baz` }
-
fixed: Routes like
/foo
and/*rest
are no longer considered
overlapping./foo
will take priority (#1086)For example:
use axum::{Router, routing::get}; let app = Router::new() // this used to not be allowed but now just works .route("/foo/*rest", get(foo)) .route("/foo/bar", get(bar)); async fn foo() {} async fn bar() {}
-
breaking: Trailing slash redirects have been removed. Previously if you
added a route for/foo
, axum would redirect calls to/foo/
to/foo
(or
vice versa for/foo/
). That is no longer supported and such requests will
now be sent to the fallback. Consider using
axum_extra::routing::RouterExt::route_with_tsr
if you want the old behavior
(#1119)For example:
use axum::{Router, routing::get}; let app = Router::new() // a request to `GET /foo/` will now get `404 Not Found` // whereas in 0.5 axum would redirect to `/foo` // // same goes the other way if you had the route `/foo/` // axum will no longer redirect from `/foo` to `/foo/` .route("/foo", get(handler)); async fn handler() {}
-
breaking:
Router::fallback
now only acceptsHandler
s (similarly to
whatget
,post
, etc accept). Use the newRouter::fallback_service
for
setting anyService
as the fallback (#1155)This fallback on 0.5:
use axum::{Router, handler::Handler}; let app = Router::new().fallback(fallback.into_service()); async fn fallback() {}
Becomes this in 0.6
use axum::Router; let app = Router::new().fallback(fallback); async fn fallback() {}
-
breaking: Allow
Error: Into<Infallible>
forRoute::{layer, route_layer}
(#924) -
breaking:
MethodRouter
now panics on overlapping routes (#1102) -
breaking:
Router::route
now only acceptsMethodRouter
s created with
get
,post
, etc. Use the newRouter::route_service
for routing to
anyService
s (#1155)
Extractors
-
added: Added new type safe
State
extractor. This can be used with
Router::with_state
and gives compile errors for missing states, whereas
Extension
would result in runtime errors (#1155)We recommend migrating from
Extension
toState
since that is more type
safe and faster. That is done by usingRouter::with_state
andState
.This setup in 0.5
use axum::{routing::get, Extension, Router}; let app = Router::new() .route("/", get(handler)) .layer(Extension(AppState {})); async fn handler(Extension(app_state): Extension<AppState>) {} #[derive(Clone)] struct AppState {}
Becomes this in 0.6 using
State
:use axum::{routing::get, extract::State, Router}; let app = Router::with_state(AppState {}) .route("/", get(handler)); async fn handler(State(app_state): State<AppState>) {} #[derive(Clone)] struct AppState {}
If you have multiple extensions you can use fields on
AppState
and implement
FromRef
:use axum::{extract::{State, FromRef}, routing::get, Router}; let state = AppState { client: HttpClient {}, database: Database {}, }; let app = Router::with_state(state).route("/", get(handler)); async fn handler( State(client): State<HttpClient>, State(database): State<Database>, ) {} #[derive(Clone)] struct AppState { client: HttpClient, database: Database, } #[derive(Clone)] struct HttpClient {} impl FromRef<AppState> for HttpClient { fn from_ref(state: &AppState) -> Self { state.client.clone() } } #[derive(Clone)] struct Database {} impl FromRef<AppState> for Database { fn from_ref(state: &AppState) -> Self { state.database.clone() } }
-
breaking: It is now only possible for one extractor per handler to consume
the request body. In 0.5 doing so would result in runtime errors but in 0.6 it
is a compile error (#1272)axum enforces this by only allowing the last extractor to consume the
request.For example:
use axum::{Json, http::HeaderMap}; // This wont compile on 0.6 because both `Json` and `String` need to consume // the request body. You can use either `Json` or `String`, but not both. async fn handler_1( json: Json<serde_json::Value>, string: String, ) {} // This won't work either since `Json` is not the last extractor. async fn handler_2( json: Json<serde_json::Value>, headers: HeaderMap, ) {} // This works! async fn handler_3( headers: HeaderMap, json: Json<serde_json::Value>, ) {}
This is done by reworking the
FromRequest
trait and introducing a new
FromRequestParts
trait.If your extractor needs to consume the request body then you should implement
FromRequest
, otherwise implementFromRequestParts
.This extractor in 0.5:
struct MyExtractor { /* ... */ } #[async_trait] impl<B> FromRequest<B> for MyExtractor where B: Send, { type Rejection = StatusCode; async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> { // ... } }
Becomes this in 0.6:
use axum::{ extract::{FromRequest, FromRequestParts}, http::{StatusCode, Request, request::Parts}, async_trait, }; struct MyExtractor { /* ... */ } // implement `FromRequestParts` if you don't need to consume the request body #[async_trait] impl<S> FromRequestParts<S> for MyExtractor where S: Send + Sync, { type Rejection = StatusCode; async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> { // ... } } // implement `FromRequest` if you do need to consume the request body #[async_trait] impl<S, B> FromRequest<S, B> for MyExtractor where S: Send + Sync, B: Send + 'static, { type Rejection = StatusCode; async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> { // ... } }
-
breaking:
RequestParts
has been removed as part of theFromRequest
rework (#1272) -
breaking:
BodyAlreadyExtracted
has been removed (#1272) -
breaking: The following types or traits have a new
S
type param
which represents the state (#1155):Router
, defaults to()
MethodRouter
, defaults to()
FromRequest
, no defaultHandler
, no default
-
added: Add
RequestExt
andRequestPartsExt
which adds convenience
methods for running extractors tohttp::Request
andhttp::request::Parts
(#1301)
Middleware
- breaking: Remove
extractor_middleware
which was previously deprecated.
Useaxum::middleware::from_extractor
instead (#1077) - added: Support running extractors on
middleware::from_fn
functions (#1088) - added: Support any middleware response that implements
IntoResponse
(#1152) - breaking: Require middleware added with
Handler::layer
to have
Infallible
as the error type (#1152)
Misc
- changed: axum's MSRV is now 1.60 (#1239)
- changed: For methods that accept some
S: Service
, the bounds have been
relaxed so the response type must implementIntoResponse
rather than being a
literalResponse
- fixed: Annotate panicking functions with
#[track_caller]
so the error
message points to where the user added the invalid route, rather than
somewhere internally in axum (#1248) - added: Add
ServiceExt
with methods for turning anyService
into a
MakeService
similarly toRouter::into_make_service
(#1302)