Releases: tokio-rs/axum
axum - v0.6.1
- added: Expand the docs for
Router::with_state
(#1580)
axum-extra - v0.4.1
- fixed: Fix wrong
From
impl forResource
(#1589)
axum - v0.6.0
Routing
-
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 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: 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 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() {}
-
changed:
Router::nest
now only acceptsRouter
s, the general-purpose
Service
nesting method has been renamed tonest_service
([#1368]) -
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]) -
breaking: Adding a
.route_layer
onto aRouter
orMethodRouter
without any routes will now result in a panic. Previously, this just did
nothing. [#1327] -
breaking:
RouterService
has been removed sinceRouter
now implements
Service
when the state is()
. UseRouter::with_state
to provide the
state and get aRouter<()>
. Note thatRouterService
only existed in the
pre-releases, not 0.5 ([#1552])
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
for sharing application state 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::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 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> { // ... } }
For an example of how to write an extractor that accepts different
Content-Types
see the [parse-body-based-on-content-type
] example. -
added:
FromRequest
andFromRequestParts
derive macro re-exports from
[axum-macros
] behind themacros
feature ([#1352]) -
added: Add
RequestExt
andRequestPartsExt
which adds convenience
methods for running extractors tohttp::Request
andhttp::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
forFailedToDeserializeQueryString
rejections, instead of422 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...
axum-macros - v0.3.0
- added: Add
#[derive(FromRequestParts)]
for deriving an implementation of
FromRequestParts
, similarly to#[derive(FromRequest)]
(#1305) - added: Add
#[derive(FromRef)]
(#1430) - added: Add
#[from_ref(skip)]
to skip implementingFromRef
for individual fields (#1537) - added: Support using a different rejection for
#[derive(FromRequest)]
with#[from_request(rejection(MyRejection))]
(#1256) - change: axum-macro's MSRV is now 1.60 (#1239)
- breaking:
#[derive(FromRequest)]
will no longer generate a rejection
enum but instead generatetype Rejection = axum::response::Response
. Use the
new#[from_request(rejection(MyRejection))]
attribute to change this.
Therejection_derive
attribute has also been removed (#1272)
axum-extra - v0.4.0
- added: Add
RouterExt::route_with_tsr
for adding routes with an
additional "trailing slash redirect" route (#1119) - added: Support chaining handlers with
HandlerCallWithExtractors::or
(#1170) - added: Add Protocol Buffer extractor and response (#1239)
- added: Add
Either*
types for combining extractors and responses into a
single type (#1263) - added:
WithRejection
extractor for customizing other extractors' rejections (#1262) - added: Add sync constructors to
CookieJar
,PrivateCookieJar
, and
SignedCookieJar
so they're easier to use in custom middleware - changed: For methods that accept some
S: Service
, the bounds have been
relaxed so the return type can be any type that implementsIntoResponse
rather than being a
literalResponse
- change: axum-extra's MSRV is now 1.60 (#1239)
- breaking:
Form
has a new rejection type (#1496) - breaking:
Query
has a new rejection type (#1496) - breaking:
Resource::nest
andResource::nest_collection
have been
removed. You can instead convert theResource
into aRouter
and
add additional routes as necessary (#1086) - breaking:
SignedCookieJar
andPrivateCookieJar
now extracts the keys
from the router's state, rather than extensions - breaking:
Resource
has a newS
type param which represents the state (#1155) - breaking:
RouterExt::route_with_tsr
now only acceptsMethodRouter
s (#1155) - added:
RouterExt::route_service_with_tsr
for routing to anyService
(#1155)
axum-core - v0.3.0
- added: Added new
FromRequestParts
trait. See axum's changelog for more
details (#1272) - breaking:
FromRequest
has been reworked andRequestParts
has been
removed. See axum's changelog for more details (#1272) - breaking:
BodyAlreadyExtracted
has been removed (#1272) - breaking:
AppendHeaders
now works on anyimpl IntoIterator
(#1495)
axum-extra - v0.4.0-rc.3
- breaking: Depend axum 0.6.0-rc.5 and axum-macros 0.3.0-rc.3
axum - v0.6.0-rc.5
-
breaking:
Router::with_state
is no longer a constructor. It is instead
used to convert the router into aRouterService
(#1532)This nested router on 0.6.0-rc.4
Router::with_state(state).route(...);
Becomes this in 0.6.0-rc.5
Router::new().route(...).with_state(state);
-
breaking::
Router::inherit_state
has been removed. Use
Router::with_state
instead (#1532) -
breaking::
Router::nest
andRouter::merge
now only supports nesting
routers that use the same state type as the router they're being merged into.
UseFromRef
for substates (#1532) -
added: Add
accept_unmasked_frames
setting in WebSocketUpgrade (#1529) -
fixed: Nested routers will now inherit fallbacks from outer routers (#1521)
-
added: Add
WebSocketUpgrade::on_failed_upgrade
to customize what to do
when upgrading a connection fails (#1539)
axum-macros - v0.3.0-rc.3
- added: Add
#[from_ref(skip)]
to skip implementingFromRef
for individual fields (#1537)
axum - v0.6.0-rc.4
- changed: The inner error of a
JsonRejection
is now
serde_path_to_error::Error<serde_json::Error>
. Previously it was
serde_json::Error
(#1371) - added:
JsonRejection
now displays the path at which a deserialization
error occurred too (#1371) - fixed: Support streaming/chunked requests in
ContentLengthLimit
(#1389) - fixed: Used
400 Bad Request
forFailedToDeserializeQueryString
rejections, instead of422 Unprocessable Entity
(#1387) - added: Add
middleware::from_extractor_with_state
and
middleware::from_extractor_with_state_arc
(#1396) - added: Add
DefaultBodyLimit::max
for changing the default body limit (#1397) - added: Add
map_request
,map_request_with_state
, and
map_request_with_state_arc
for transforming the request with an async
function (#1408) - added: Add
map_response
,map_response_with_state
, and
map_response_with_state_arc
for transforming the response with an async
function (#1414) - breaking:
ContentLengthLimit
has been removed.Use DefaultBodyLimit
instead (#1400) - changed:
Router
no longer implementsService
, call.into_service()
on it to obtain aRouterService
that does (#1368) - added: Add
Router::inherit_state
, which creates aRouter
with an
arbitrary state type without actually supplying the state; such aRouter
can't be turned into a service directly (.into_service()
will panic), but
can be nested or merged into aRouter
with the same state type (#1368) - changed:
Router::nest
now only acceptsRouter
s, the general-purpose
Service
nesting method has been renamed tonest_service
(#1368) - added: Support compiling to WASM. See the
simple-router-wasm
example
for more details (#1382) - breaking: New
tokio
default feature needed for WASM support. If you
don't need WASM support but havedefault_features = false
for other reasons
you likely need to re-enable thetokio
feature (#1382) - breaking:
handler::{WithState, IntoService}
are merged into one type,
namedHandlerService
(#1418) - changed: The default body limit now applies to the
Multipart
extractor (#1420) - added: String and binary
From
impls have been added toextract::ws::Message
to be more inline withtungstenite
(#1421) - added: Add
#[derive(axum::extract::FromRef)]
([#1430]) - added:
FromRequest
andFromRequestParts
derive macro re-exports from
[axum-macros
] behind themacros
feature (#1352) - breaking:
MatchedPath
can now no longer be extracted in middleware for
nested routes (#1462) - added: Add
extract::RawForm
for accessing raw urlencoded query bytes or request body (#1487) - breaking: Rename
FormRejection::FailedToDeserializeQueryString
to
FormRejection::FailedToDeserializeForm
(#1496)