axum/CHANGELOG.md

18 KiB

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Unreleased

  • fixed: All known compile time issues are resolved, including those with boxed and those introduced by Rust 1.56 (#404)

  • Big internal refactoring of routing leading to several improvements (#363)

    • added: Wildcard routes like .route("/api/users/*rest", service) are now supported.
    • fixed: The order routes are added in no longer matters.
    • fixed: Adding a conflicting route will now cause a panic instead of silently making a route unreachable.
    • fixed: Route matching is faster as number of routes increase.
    • breaking: The routes /foo and /:key are considered to overlap and will cause a panic when constructing the router. This might be fixed in the future.
  • fixed: Expand accepted content types for JSON requests (#378)

  • breaking: The router's type is now always Router regardless of how many routes or middleware are applies (#404)

    This means router types are all always nameable:

    fn my_routes() -> Router {
        Router::new().route(
            "/users",
            post(|| async { "Hello, World!" }),
        )
    }
    
  • breaking: Route::boxed and BoxRoute have been removed as they're no longer necessary (#404)

  • breaking: Route, Nested, Or types are now private. They no longer had to be public because Router is internally boxed (#404)

  • breaking: Automatically do percent decoding in extract::Path (#272)

  • breaking: Added feature flags for HTTP1 and JSON. This enables removing a few dependencies if your app only uses HTTP2 or doesn't use JSON. Its only a breaking change if you depend on axum with default_features = false. (#286)

  • breaking: Remove routing::Layered as it didn't actually do anything and thus wasn't necessary

  • breaking: Change Connected::connect_info to return Self and remove the associated type ConnectInfo (#396)

  • breaking: Simplify error handling model (#402):

    • All services part of the router are now required to be infallible.
    • Error handling utilities have been moved to an error_handling module.
    • Router::check_infallible has been removed since routers are always infallible with the error handling changes.
    • Error handling closures must now handle all errors and thus always return something that implements IntoResponse.

    With these changes handling errors from fallible middleware is done like so:

    use axum::{
        handler::get,
        http::StatusCode,
        error_handling::HandleErrorLayer,
        response::IntoResponse,
        Router, BoxError,
    };
    use tower::ServiceBuilder;
    use std::time::Duration;
    
    let middleware_stack = ServiceBuilder::new()
        // Handle errors from middleware
        //
        // This middleware most be added above any fallible
        // ones if you're using `ServiceBuilder`, due to how ordering works
        .layer(HandleErrorLayer::new(handle_error))
        // Return an error after 30 seconds
        .timeout(Duration::from_secs(30));
    
    let app = Router::new()
        .route("/", get(|| async { /* ... */ }))
        .layer(middleware_stack);
    
    fn handle_error(_error: BoxError) -> impl IntoResponse {
        StatusCode::REQUEST_TIMEOUT
    }
    

    And handling errors from fallible leaf services is done like so:

    use axum::{
        Router, service,
        body::Body,
        handler::get,
        response::IntoResponse,
        http::{Request, Response},
        error_handling::HandleErrorExt, // for `.handle_error`
    };
    use std::{io, convert::Infallible};
    use tower::service_fn;
    
    let app = Router::new()
        .route(
            "/",
            service::get(service_fn(|_req: Request<Body>| async {
                let contents = tokio::fs::read_to_string("some_file").await?;
                Ok::<_, io::Error>(Response::new(Body::from(contents)))
            }))
            .handle_error(handle_io_error),
        );
    
    fn handle_io_error(error: io::Error) -> impl IntoResponse {
        // ...
    }
    

0.2.8 (07. October, 2021)

  • Document debugging handler type errors with "axum-debug" (#372)

0.2.7 (06. October, 2021)

  • Bump minimum version of async-trait (#370)

0.2.6 (02. October, 2021)

  • Clarify that handler::any and service::any only accepts standard HTTP methods (#337)
  • Document how to customize error responses from extractors (#359)

0.2.5 (18. September, 2021)

  • Add accessors for TypedHeaderRejection fields (#317)
  • Improve docs for extractors (#327)

0.2.4 (10. September, 2021)

  • Document using StreamExt::split with WebSocket (#291)
  • Document adding middleware to multiple groups of routes (#293)

0.2.3 (26. August, 2021)

  • fixed: Fix accidental breaking change introduced by internal refactor. BoxRoute used to be Sync but was accidental made !Sync (#273)

0.2.2 (26. August, 2021)

  • fixed: Fix URI captures matching empty segments. This means requests with URI / will no longer be matched by /:key (#264)
  • fixed: Remove needless trait bounds from Router::boxed (#269)

0.2.1 (24. August, 2021)

  • added: Add Redirect::to constructor (#255)
  • added: Document how to implement IntoResponse for custom error type (#258)

0.2.0 (23. August, 2021)

  • Overall:

    • fixed: Overall compile time improvements. If you're having issues with compile time please file an issue! (#184) (#198) (#220)
    • changed: Remove prelude. Explicit imports are now required (#195)
  • Routing:

    • added: Add dedicated Router to replace the RoutingDsl trait (#214)
    • added: Add Router::or for combining routes (#108)
    • fixed: Support matching different HTTP methods for the same route that aren't defined together. So Router::new().route("/", get(...)).route("/", post(...)) now accepts both GET and POST. Previously only POST would be accepted (#224)
    • fixed: get routes will now also be called for HEAD requests but will always have the response body removed (#129)
    • changed: Replace axum::route(...) with axum::Router::new().route(...). This means there is now only one way to create a new router. Same goes for axum::routing::nest. (#215)
    • changed: Implement routing::MethodFilter via bitflags (#158)
    • changed: Move handle_error from ServiceExt to service::OnMethod (#160)

    With these changes this app using 0.1:

    use axum::{extract::Extension, prelude::*, routing::BoxRoute, AddExtensionLayer};
    
    let app = route("/", get(|| async { "hi" }))
        .nest("/api", api_routes())
        .layer(AddExtensionLayer::new(state));
    
    fn api_routes() -> BoxRoute<Body> {
        route(
            "/users",
            post(|Extension(state): Extension<State>| async { "hi from nested" }),
        )
        .boxed()
    }
    

    Becomes this in 0.2:

    use axum::{
        extract::Extension,
        handler::{get, post},
        routing::BoxRoute,
        Router,
    };
    
    let app = Router::new()
        .route("/", get(|| async { "hi" }))
        .nest("/api", api_routes());
    
    fn api_routes() -> Router<BoxRoute> {
        Router::new()
            .route(
                "/users",
                post(|Extension(state): Extension<State>| async { "hi from nested" }),
            )
            .boxed()
    }
    
  • Extractors:

    • added: Make FromRequest default to being generic over body::Body (#146)
    • added: Implement std::error::Error for all rejections (#153)
    • added: Add OriginalUri for extracting original request URI in nested services (#197)
    • added: Implement FromRequest for http::Extensions (#169)
    • added: Make RequestParts::{new, try_into_request} public so extractors can be used outside axum (#194)
    • added: Implement FromRequest for axum::body::Body (#241)
    • changed: Removed extract::UrlParams and extract::UrlParamsMap. Use extract::Path instead (#154)
    • changed: extractor_middleware now requires RequestBody: Default (#167)
    • changed: Convert RequestAlreadyExtracted to an enum with each possible error variant (#167)
    • changed: extract::BodyStream is no longer generic over the request body (#234)
    • changed: extract::Body has been renamed to extract::RawBody to avoid conflicting with body::Body (#233)
    • changed: RequestParts changes (#153)
      • method new returns an &http::Method
      • method_mut new returns an &mut http::Method
      • take_method has been removed
      • uri new returns an &http::Uri
      • uri_mut new returns an &mut http::Uri
      • take_uri has been removed
    • changed: Remove several rejection types that were no longer used (#153) (#154)
  • Responses:

    • added: Add Headers for easily customizing headers on a response (#193)
    • added: Add Redirect response (#192)
    • added: Add body::StreamBody for easily responding with a stream of byte chunks (#237)
    • changed: Add associated Body and BodyError types to IntoResponse. This is required for returning responses with bodies other than hyper::Body from handlers. See the docs for advice on how to implement IntoResponse (#86)
    • changed: tower::util::Either no longer implements IntoResponse (#229)

    This IntoResponse from 0.1:

    use axum::{http::Response, prelude::*, response::IntoResponse};
    
    struct MyResponse;
    
    impl IntoResponse for MyResponse {
        fn into_response(self) -> Response<Body> {
            Response::new(Body::empty())
        }
    }
    

    Becomes this in 0.2:

    use axum::{body::Body, http::Response, response::IntoResponse};
    
    struct MyResponse;
    
    impl IntoResponse for MyResponse {
        type Body = Body;
        type BodyError = <Self::Body as axum::body::HttpBody>::Error;
    
        fn into_response(self) -> Response<Self::Body> {
            Response::new(Body::empty())
        }
    }
    
  • SSE:

    • added: Add response::sse::Sse. This implements SSE using a response rather than a service (#98)
    • changed: Remove axum::sse. Its been replaced by axum::response::sse (#98)

    Handler using SSE in 0.1:

    use axum::{
        prelude::*,
        sse::{sse, Event},
    };
    use std::convert::Infallible;
    
    let app = route(
        "/",
        sse(|| async {
            let stream = futures::stream::iter(vec![Ok::<_, Infallible>(
                Event::default().data("hi there!"),
            )]);
            Ok::<_, Infallible>(stream)
        }),
    );
    

    Becomes this in 0.2:

    use axum::{
        handler::get,
        response::sse::{Event, Sse},
        Router,
    };
    use std::convert::Infallible;
    
    let app = Router::new().route(
        "/",
        get(|| async {
            let stream = futures::stream::iter(vec![Ok::<_, Infallible>(
                Event::default().data("hi there!"),
            )]);
            Sse::new(stream)
        }),
    );
    
  • WebSockets:

    • changed: Change WebSocket API to use an extractor plus a response (#121)
    • changed: Make WebSocket Message an enum (#116)
    • changed: WebSocket now uses Error as its error type (#150)

    Handler using WebSockets in 0.1:

    use axum::{
        prelude::*,
        ws::{ws, WebSocket},
    };
    
    let app = route(
        "/",
        ws(|socket: WebSocket| async move {
            // do stuff with socket
        }),
    );
    

    Becomes this in 0.2:

    use axum::{
        extract::ws::{WebSocket, WebSocketUpgrade},
        handler::get,
        Router,
    };
    
    let app = Router::new().route(
        "/",
        get(|ws: WebSocketUpgrade| async move {
            ws.on_upgrade(|socket: WebSocket| async move {
                // do stuff with socket
            })
        }),
    );
    
  • Misc

    • added: Add default feature tower-log which exposes tower's log feature. (#218)
    • changed: Replace body::BoxStdError with axum::Error, which supports downcasting (#150)
    • changed: EmptyRouter now requires the response body to implement Send + Sync + 'static' (#108)
    • changed: Router::check_infallible now returns a CheckInfallible service. This is to improve compile times (#198)
    • changed: Router::into_make_service now returns routing::IntoMakeService rather than tower::make::Shared (#229)
    • changed: All usage of tower::BoxError has been replaced with axum::BoxError (#229)
    • changed: Several response future types have been moved into dedicated future modules (#133)
    • changed: EmptyRouter, ExtractorMiddleware, ExtractorMiddlewareLayer, and QueryStringMissing no longer implement Copy (#132)
    • changed: service::OnMethod, handler::OnMethod, and routing::Nested have new response future types (#157)

0.1.3 (06. August, 2021)

  • Fix stripping prefix when nesting services at / (#91)
  • Add support for WebSocket protocol negotiation (#83)
  • Use pin-project-lite instead of pin-project (#95)
  • Re-export http crate and hyper::Server (#110)
  • Fix Query and Form extractors giving bad request error when query string is empty. (#117)
  • Add Path extractor. (#124)
  • Fixed the implementation of IntoResponse of (HeaderMap, T) and (StatusCode, HeaderMap, T) would ignore headers from T (#137)
  • Deprecate extract::UrlParams and extract::UrlParamsMap. Use extract::Path instead (#138)

0.1.2 (01. August, 2021)

  • Implement Stream for WebSocket (#52)
  • Implement Sink for WebSocket (#52)
  • Implement Deref most extractors (#56)
  • Return 405 Method Not Allowed for unsupported method for route (#63)
  • Add extractor for remote connection info (#55)
  • Improve error message of MissingExtension rejections (#72)
  • Improve documentation for routing (#71)
  • Clarify required response body type when routing to tower::Services (#69)
  • Add axum::body::box_body to converting an http_body::Body to axum::body::BoxBody (#69)
  • Add axum::sse for Server-Sent Events (#75)
  • Mention required dependencies in docs (#77)
  • Fix WebSockets failing on Firefox (#76)

0.1.1 (30. July, 2021)

  • Misc readme fixes.

0.1.0 (30. July, 2021)

  • Initial release.