-
fixed: Nested routers are now allowed to have fallbacks (#1521):
let api_router = Router::new()
.route("/users", get(|| { ... }))
.fallback(api_fallback);
let app = Router::new()
// this would panic in 0.5 but in 0.6 it just works
//
// requests starting with `/api` but not handled by `api_router`
// will go to `/api_fallback`
.nest("/api", api_router);
The outer router's fallback will still apply if a nested router doesn't have
its own fallback:
// this time without a fallback
let api_router = Router::new().route("/users", get(|| { ... }));
let app = Router::new()
.nest("/api", api_router)
// `api_fallback` will inherit this fallback
.fallback(app_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 of foo.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: Automatic 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/
):
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() {}
Either explicitly add routes for /foo
and /foo/
or use
axum_extra::routing::RouterExt::route_with_tsr
if you want the old behavior
(#1119)
-
breaking: Router::fallback
now only accepts Handler
s (similarly to
what get
, post
, etc. accept). Use the new Router::fallback_service
for
setting any Service
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() {}
-
changed: Router::nest
now only accepts Router
s, the general-purpose
Service
nesting method has been renamed to nest_service
(#1368)
-
breaking: Allow Error: Into<Infallible>
for Route::{layer, route_layer}
(#924)
-
breaking: MethodRouter
now panics on overlapping routes (#1102)
-
breaking: Router::route
now only accepts MethodRouter
s created with
get
, post
, etc. Use the new Router::route_service
for routing to
any Service
s (#1155)
-
breaking: Adding a .route_layer
onto a Router
or MethodRouter
without any routes will now result in a panic. Previously, this just did
nothing. #1327
-
breaking: RouterService
has been removed since Router
now implements
Service
when the state is ()
. Use Router::with_state
to provide the
state and get a Router<()>
. Note that RouterService
only existed in the
pre-releases, not 0.5 (#1552)
-
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
to State
for sharing application state since that is more type
safe and faster. That is done by using Router::with_state
and State
.
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::new()
.route("/", get(handler))
.with_state(AppState {});
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::new().route("/", get(handler)).with_state(state);
async fn handler(
State(client): State<HttpClient>,
State(database): State<Database>,
) {}
// the derive requires enabling the "macros" feature
#[derive(Clone, FromRef)]
struct AppState {
client: HttpClient,
database: Database,
}
#[derive(Clone)]
struct HttpClient {}
#[derive(Clone)]
struct Database {}
-
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 implement FromRequestParts
.
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> {
// ...
}
}
For an example of how to write an extractor that accepts different
Content-Types
see the [parse-body-based-on-content-type
][parse-body-based-on-content-type] example.
-
added: FromRequest
and FromRequestParts
derive macro re-exports from
[axum-macros
][axum-macros] behind the macros
feature (#1352)
-
added: Add RequestExt
and RequestPartsExt
which adds convenience
methods for running extractors to http::Request
and http::request::Parts
(#1301)
-
added: JsonRejection
now displays the path at which a deserialization
error occurred (#1371)
-
added: Add extract::RawForm
for accessing raw urlencoded query bytes or request body (#1487)
-
fixed: Used 400 Bad Request
for FailedToDeserializeQueryString
rejections, instead of 422 Unprocessable Entity
(#1387)
-
changed: The inner error of a JsonRejection
is now
serde_path_to_error::Error<serde_json::Error>
. Previously it was
serde_json::Error
(#1371)
-
changed: The default body limit now applies to the Multipart
extractor (#1420)
-
breaking: ContentLengthLimit
has been removed. Use DefaultBodyLimit
instead (#1400)
-
breaking: RequestParts
has been removed as part of the FromRequest
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 default
Handler
, no default
-
breaking: MatchedPath
can now no longer be extracted in middleware for
nested routes. In previous versions it returned invalid data when extracted
from a middleware applied to a nested router. MatchedPath
can still be
extracted from handlers and middleware that aren't on nested routers (#1462)
-
breaking: Rename FormRejection::FailedToDeserializeQueryString
to
FormRejection::FailedToDeserializeForm
(#1496)
Note: This is a re-release of 0.5.14 that fixes an accidental breaking change.
Yanked, as it contained an accidental breaking change.
Yanked, as it contained an accidental breaking change.