github tokio-rs/axum axum-v0.6.0-rc.1
axum - v0.6.0-rc.1

latest releases: axum-v0.8.0-alpha.1, axum-extra-v0.10.0-alpha.1, axum-macros-v0.5.0-alpha.1...
pre-release2 years ago

Routing

  • breaking: Nested Routers will no longer delegate to the outer Router's
    fallback. Instead you must explicitly set a fallback on the inner Router (#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 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: 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 accepts Handlers (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() {}
  • 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 MethodRouters created with
    get, post, etc. Use the new Router::route_service for routing to
    any Services (#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 to 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::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 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> {
            // ...
        }
    }
  • 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
  • added: Add RequestExt and RequestPartsExt which adds convenience
    methods for running extractors to http::Request and http::request::Parts (#1301)

Middleware

  • breaking: Remove extractor_middleware which was previously deprecated.
    Use axum::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 implement IntoResponse rather than being a
    literal Response
  • 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 any Service into a
    MakeService similarly to Router::into_make_service (#1302)

Don't miss a new axum release

NewReleases is sending notifications on new releases.