This reworks axum's docs in an attempt to make things easier to find. Previously I wasn't a fan of those docs for the same topic were spread across the root module docs and more specific places like types and methods. This changes it such that the root module docs only gives a high level introduction to a topic, perhaps with a small example, and then link to other places where all the details are. This means `Router` is now the single place to learn about routing, and etc for the topics like handlers and error handling.
20 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
- Overall:
-
fixed: All known compile time issues are resolved, including those with
boxedand those introduced by Rust 1.56 (#404) -
breaking: The router's type is now always
Routerregardless 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: 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:
Route::boxedandBoxRoutehave been removed as they're no longer necessary (#404) -
breaking:
Route,Nested,Ortypes are now private. They no longer had to be public becauseRouteris internally boxed (#404) -
breaking: Remove
routing::Layeredas it didn't actually do anything and thus wasn't necessary -
breaking: Vendor
AddExtensionLayerandAddExtensionto reduce public dependencies
-
- Routing:
- 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.
- added: Wildcard routes like
- fixed: Correctly handle trailing slashes in routes:
- If a route with a trailing slash exists and a request without a trailing slash is received, axum will send a 301 redirection to the route with the trailing slash.
- Or vice versa if a route without a trailing slash exists and a request with a trailing slash is received.
- This can be overridden by explicitly defining two routes: One with and one without trailing a slash.
- breaking: Method routing for handlers have been moved from
axum::handlertoaxum::routing. Soaxum::handler::getnow lives ataxum::routing::get(#405) - breaking: Method routing for services have been moved from
axum::servicetoaxum::routing. Soaxum::service::getnow lives at, etc.axum::routing::service_method_routing::get, etc. (#405) - breaking:
Router::orrenamed toRouter::mergeand will now panic on overlapping routes. It now only acceptsRouters and not generalServices. UseRouter::fallbackfor adding fallback routes (#408) - added:
Router::fallbackfor adding handlers for request that didn't match any routes.Router::fallbackmust be use instead ofnest("/", _)(#408) - breaking:
EmptyRouterhas been renamed toMethodNotAllowedas its only used in method routers and not in path routers (Router) - breaking: Remove support for routing based on the
CONNECTmethod. An example of combining axum with and HTTP proxy can be found here (#428)
- Big internal refactoring of routing leading to several improvements (#363)
- Extractors:
- fixed: Expand accepted content types for JSON requests (#378)
- fixed: Support deserializing
i128andu128inextract::Path - breaking: Automatically do percent decoding in
extract::Path(#272) - breaking: Change
Connected::connect_infoto returnSelfand remove the associated typeConnectInfo(#396) - added: Add
extract::MatchedPathfor accessing path in router that matched request (#412)
- Error handling:
-
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_handlingmodule. Router::check_infalliblehas 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::{ routing::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, routing::service_method_routing::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( "/", 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 { // ... }
-
- Misc:
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::anyandservice::anyonly accepts standard HTTP methods (#337) - Document how to customize error responses from extractors (#359)
0.2.5 (18. September, 2021)
0.2.4 (10. September, 2021)
- Document using
StreamExt::splitwithWebSocket(#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.
BoxRouteused to beSyncbut 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::toconstructor (#255) - added: Document how to implement
IntoResponsefor custom error type (#258)
0.2.0 (23. August, 2021)
-
Overall:
-
Routing:
- added: Add dedicated
Routerto replace theRoutingDsltrait (#214) - added: Add
Router::orfor 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 bothGETandPOST. Previously onlyPOSTwould be accepted (#224) - fixed:
getroutes will now also be called forHEADrequests but will always have the response body removed (#129) - changed: Replace
axum::route(...)withaxum::Router::new().route(...). This means there is now only one way to create a new router. Same goes foraxum::routing::nest. (#215) - changed: Implement
routing::MethodFilterviabitflags(#158) - changed: Move
handle_errorfromServiceExttoservice::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() } - added: Add dedicated
-
Extractors:
- added: Make
FromRequestdefault to being generic overbody::Body(#146) - added: Implement
std::error::Errorfor all rejections (#153) - added: Add
OriginalUrifor extracting original request URI in nested services (#197) - added: Implement
FromRequestforhttp::Extensions(#169) - added: Make
RequestParts::{new, try_into_request}public so extractors can be used outside axum (#194) - added: Implement
FromRequestforaxum::body::Body(#241) - changed: Removed
extract::UrlParamsandextract::UrlParamsMap. Useextract::Pathinstead (#154) - changed:
extractor_middlewarenow requiresRequestBody: Default(#167) - changed: Convert
RequestAlreadyExtractedto an enum with each possible error variant (#167) - changed:
extract::BodyStreamis no longer generic over the request body (#234) - changed:
extract::Bodyhas been renamed toextract::RawBodyto avoid conflicting withbody::Body(#233) - changed:
RequestPartschanges (#153)methodnew returns an&http::Methodmethod_mutnew returns an&mut http::Methodtake_methodhas been removedurinew returns an&http::Uriuri_mutnew returns an&mut http::Uritake_urihas been removed
- changed: Remove several rejection types that were no longer used (#153) (#154)
- added: Make
-
Responses:
- added: Add
Headersfor easily customizing headers on a response (#193) - added: Add
Redirectresponse (#192) - added: Add
body::StreamBodyfor easily responding with a stream of byte chunks (#237) - changed: Add associated
BodyandBodyErrortypes toIntoResponse. This is required for returning responses with bodies other thanhyper::Bodyfrom handlers. See the docs for advice on how to implementIntoResponse(#86) - changed:
tower::util::Eitherno longer implementsIntoResponse(#229)
This
IntoResponsefrom 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()) } } - added: Add
-
SSE:
- added: Add
response::sse::Sse. This implements SSE using a response rather than a service (#98) - changed: Remove
axum::sse. Its been replaced byaxum::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) }), ); - added: Add
-
WebSockets:
- changed: Change WebSocket API to use an extractor plus a response (#121)
- changed: Make WebSocket
Messagean enum (#116) - changed:
WebSocketnow usesErroras 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-logwhich exposestower'slogfeature. (#218) - changed: Replace
body::BoxStdErrorwithaxum::Error, which supports downcasting (#150) - changed:
EmptyRouternow requires the response body to implementSend + Sync + 'static'(#108) - changed:
Router::check_infalliblenow returns aCheckInfallibleservice. This is to improve compile times (#198) - changed:
Router::into_make_servicenow returnsrouting::IntoMakeServicerather thantower::make::Shared(#229) - changed: All usage of
tower::BoxErrorhas been replaced withaxum::BoxError(#229) - changed: Several response future types have been moved into dedicated
futuremodules (#133) - changed:
EmptyRouter,ExtractorMiddleware,ExtractorMiddlewareLayer, andQueryStringMissingno longer implementCopy(#132) - changed:
service::OnMethod,handler::OnMethod, androuting::Nestedhave new response future types (#157)
- added: Add default feature
0.1.3 (06. August, 2021)
- Fix stripping prefix when nesting services at
/(#91) - Add support for WebSocket protocol negotiation (#83)
- Use
pin-project-liteinstead ofpin-project(#95) - Re-export
httpcrate andhyper::Server(#110) - Fix
QueryandFormextractors giving bad request error when query string is empty. (#117) - Add
Pathextractor. (#124) - Fixed the implementation of
IntoResponseof(HeaderMap, T)and(StatusCode, HeaderMap, T)would ignore headers fromT(#137) - Deprecate
extract::UrlParamsandextract::UrlParamsMap. Useextract::Pathinstead (#138)
0.1.2 (01. August, 2021)
- Implement
StreamforWebSocket(#52) - Implement
SinkforWebSocket(#52) - Implement
Derefmost extractors (#56) - Return
405 Method Not Allowedfor unsupported method for route (#63) - Add extractor for remote connection info (#55)
- Improve error message of
MissingExtensionrejections (#72) - Improve documentation for routing (#71)
- Clarify required response body type when routing to
tower::Services (#69) - Add
axum::body::box_bodyto converting anhttp_body::Bodytoaxum::body::BoxBody(#69) - Add
axum::ssefor 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.