- Overall:
- **fixed:** All known compile time issues are resolved, including those with
`boxed` and those introduced by Rust 1.56 ([#404])
- **breaking:** The router's type is now always `Router` regardless of how many routes or
middleware are applied ([#404])
This means router types are all always nameable:
```rust
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::boxed` and `BoxRoute` have been removed as they're no longer
necessary ([#404])
- **breaking:** `Nested`, `Or` types are now private. They no longer had to be
public because `Router` is internally boxed ([#404])
- **breaking:** Remove `routing::Layered` as it didn't actually do anything and
thus wasn't necessary
- **breaking:** Vendor `AddExtensionLayer` and `AddExtension` to reduce public
dependencies
- **breaking:** `body::BoxBody` is now a type alias for
`http_body::combinators::UnsyncBoxBody` and thus is no longer `Sync`. This
is because bodies are streams and requiring streams to be `Sync` is
unnecessary.
- **added:** Implement `IntoResponse` for `http_body::combinators::UnsyncBoxBody`.
- **added:** Add `Handler::into_make_service` for serving a handler without a
`Router`.
- **added:** Add `Handler::into_make_service_with_connect_info` for serving a
handler without a `Router`, and storing info about the incoming connection.
- **breaking:** axum's minimum support rust version is not 1.54
- 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.
- **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::handler`
to `axum::routing`. So `axum::handler::get` now lives at `axum::routing::get`
([#405])
- **breaking:** Method routing for services have been moved from `axum::service`
to `axum::routing`. So `axum::service::get` now lives at, etc.
`axum::routing::service_method_routing::get`, etc. ([#405])
- **breaking:** `Router::or` renamed to `Router::merge` and will now panic on
overlapping routes. It now only accepts `Router`s and not general `Service`s.
Use `Router::fallback` for adding fallback routes ([#408])
- **added:** `Router::fallback` for adding handlers for request that didn't
match any routes. `Router::fallback` must be use instead of `nest("/", _)` ([#408])
- **breaking:** `EmptyRouter` has been renamed to `MethodNotAllowed` as its only
used in method routers and not in path routers (`Router`)
- **breaking:** Remove support for routing based on the `CONNECT` method. An
example of combining axum with and HTTP proxy can be found [here][proxy] ([#428])
- Extractors:
- **fixed:** Expand accepted content types for JSON requests ([#378])
- **fixed:** Support deserializing `i128` and `u128` in `extract::Path`
- **breaking:** Automatically do percent decoding in `extract::Path`
([#272])
- **breaking:** Change `Connected::connect_info` to return `Self` and remove
the associated type `ConnectInfo` ([#396])
- **added:** Add `extract::MatchedPath` for accessing path in router that
matched the 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_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:
```rust,no_run
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:
```rust
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:
- `InvalidWebsocketVersionHeader` has been renamed to `InvalidWebSocketVersionHeader` ([#416])
- `WebsocketKeyHeaderMissing` has been renamed to `WebSocketKeyHeaderMissing` ([#416])
[#339]: https://github.com/tokio-rs/axum/pull/339
[#286]: https://github.com/tokio-rs/axum/pull/286
[#272]: https://github.com/tokio-rs/axum/pull/272
[#378]: https://github.com/tokio-rs/axum/pull/378
[#363]: https://github.com/tokio-rs/axum/pull/363
[#396]: https://github.com/tokio-rs/axum/pull/396
[#402]: https://github.com/tokio-rs/axum/pull/402
[#404]: https://github.com/tokio-rs/axum/pull/404
[#405]: https://github.com/tokio-rs/axum/pull/405
[#408]: https://github.com/tokio-rs/axum/pull/408
[#412]: https://github.com/tokio-rs/axum/pull/412
[#416]: https://github.com/tokio-rs/axum/pull/416
[#428]: https://github.com/tokio-rs/axum/pull/428
[proxy]: https://github.com/tokio-rs/axum/blob/main/examples/http-proxy/src/main.rs
* Make `IntoMakeService(WithConnectInfo)?` work with any Service
* Add `Handler::{into_make_service, into_make_service_with_connect_info}`
These are useful if you want to run a handler without a `Router`, for
example to make a proxy.
As we learned [in Tonic] bodies don't need to be `Sync` because they can
only be polled from one thread at a time.
This changes axum's bodies to no longer require `Sync` and makes
`BoxBody` an alias for `UnsyncBoxBody<Bytes, axum::Error>`.
[in Tonic]: https://github.com/hyperium/tonic/issues/117
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.
axum uses `impl Layer for ServiceBuilder` internally which requires
tower 0.4.10. Making that explicit should help users updating to axum
0.3 without also having to manually bump their tower dependency.
For 0.3 I'm thinking about some changes I wanna make to the docs. I
don't like how information is currently spread over so many places.
Still thinking about how I wanna re-organize it.
However I do think it makes sense to break the root module docs into
separate files that get included with `#![doc = include_str!("file")]`.
Makes working on a single section easier and more focused. It looks the
same for the user reading the docs.
This means axum's MSRV is now 1.54 but since thats two releases ago I'm
fine with that.
This panics if you pass a `Router` to `Router::route`. Thats invalid and
will result in unreachable routes.
Unfortunately I don't think we can make it a type error while supporting
general `Service`s. So I think this is a decent workaround.
Fixes https://github.com/tokio-rs/axum/issues/174
When fixing bugs with `MatchedPath` (introduced to fix https://github.com/tokio-rs/axum/issues/386) I realized nesting could basically be implemented using regular routes, if we can detect that the service passed to `nest` is in fact a `Router`. Then we can transfer over all its routes and add the prefix.
This makes nesting much simpler in general and should also be slightly faster since we're no longer nesting routers.
We stripped the prefix before calling any middleware. Instead the prefix
should be stripped just before calling the actual route. This way
middleware still see the full URI, as it used to work in 0.2.
Fixes#419
While thinking about #419 I realized that `main` had a bug where
middleware wouldn't be called if no route matched the incoming request.
`Router` would just directly return a 404 without calling any
middleware.
This fixes by making the fallback default to a service that always
returns 404 and always calling that if no route matches.
Layers applied to the router is then also applied to the fallback.
Unfortunately this breaks #380 but I don't currently see a way to
support both. Auth middleware need to run _after_ routing because you
don't care about auth for unknown paths, but logging middleware need to
run _before_ routing because they do care about seeing requests for
unknown paths so they can log them...
Part of #419
- Static vs dynamic paths are now supported meaning `/foo` and `/:key`
are not considered to overlap.
- A bug we hit regarding trailing slashes is fixed.
This remove tower-http from axum's public API. I would like to be able
to make breaking releases of tower-http without also having to ship a
breaking release of axum.
With https://github.com/tokio-rs/axum/pull/404 and https://github.com/tokio-rs/axum/pull/402 all routes now have the same types and thus we don't need to nest them but can instead store them all in a map. This simplifies the routing quite a bit and is faster as well.
High level changes:
- Routes are now stored in a `HashMap<RouteId, Route<B>>`.
- `Router::or` is renamed to `Router::merge` because thats what it does now. It copies all routes from one router to another. This also means overlapping routes will cause a panic which is nice win.
- `Router::merge` now only accepts `Router`s so added `Router::fallback` for adding a global 404 handler.
- The `Or` service has been removed.
- `Router::layer` now only adds layers to the routes you actually have meaning middleware runs _after_ routing. I believe that addresses https://github.com/tokio-rs/axum/issues/380 but will test that on another branch.
In #363 I added `MaybeSharedNode` which makes `Router` faster to clone
_if_ you're using `into_make_service`. However I would like instead like
to find a solution that works even when you're not using
`into_make_service`.
Using an `Arc<RwLock<Node<_>>>` is probably the only solution but would
like to experiment. For now I'm gonna rollback `MaybeSharedNode`.
Changing it wont require user facing changes.
* "matchit" based router
* Update changelog
* Remove dependency on `regex`
* Docs
* Fix typos
* Also mention route order in root module docs
* Update CHANGELOG.md
Co-authored-by: Jonas Platte <jplatte@users.noreply.github.com>
* Document that `/:key` and `/foo` overlaps
* Provide good error message for wildcards in routes
* minor clean ups
* Make `Router` cheaper to clone
* Ensure middleware still only applies to routes above
* Remove call to issues from changelog
We're aware of the short coming :)
* Fix tests on 1.51
Co-authored-by: Jonas Platte <jplatte@users.noreply.github.com>
I've been thinking that having an associated type probably isn't
necessary. I imagine most users are either using `SocketAddr` to the
remote connection IP, or writing their own connection struct.