* Handle structs without fields
* Support opt-out of derived rejection traits
* Handle duplicate opt outs
* Improve error if opting out of `Display` or `Debug` but not `Error`
* document `rejection_derive`
* Handle using both `via` and `rejection_derive`
* don't derive debug for `RejectionDeriveOptOuts`
* Update axum-macros/src/from_request.rs
Co-authored-by: Jonas Platte <[email protected]>
Co-authored-by: Jonas Platte <[email protected]>
* Move axum-debug into axum-macros
* fix ref to axum-macros in changelog
* Apply suggestions from code review
Co-authored-by: Jonas Platte <[email protected]>
Co-authored-by: Jonas Platte <[email protected]>
* initial working impl
* support `#[from_request(via(...))]`
* support extracting the whole thing at once
* rely on type inference
* fix footgun
* fix typo
* generate rejection enums
* move tests to trybuild
* minor clean up
* docs
* Support multiple generic extractors with same "via" type
* support `Result` as well
* Update axum-macros/src/from_request.rs
Co-authored-by: Jonas Platte <[email protected]>
* Add `#[automatically_derived]`
* remove needless `#[derive(Debug)]` on macro types
* Fix error messages that different for some reason
* Update axum-macros/src/lib.rs
Co-authored-by: Jonas Platte <[email protected]>
* add more `#[automatically_derived]`
* support same types in tuple structs
* update docs
* prep axum-macros for release
* address review feedback
* Update axum-macros/src/lib.rs
Co-authored-by: Jonas Platte <[email protected]>
* Update axum-macros/src/lib.rs
Co-authored-by: Jonas Platte <[email protected]>
* Update known limitation
Co-authored-by: Jonas Platte <[email protected]>
* Remove `RequestParts::take_extensions`
* fix out of date docs
* Remove RequestAlreadyExtracted and replace it with BodyAlreadyExtracted
* fix docs
* fix test
* Update axum-core/src/extract/mod.rs
Co-authored-by: Jonas Platte <[email protected]>
* Remove macro only used once
Co-authored-by: Jonas Platte <[email protected]>
* Change `HeaderMap` extractor to clone the headers
* fix docs
* changelog
* inline variable
* also add changelog item to axum
* don't list types from axum in axum-core's changelog
* document that `HeaderMap::from_request` clones the headers
* fix typo
* a few more typos
- **fixed:** Fix using incorrect path prefix when nesting `Router`s at `/` ([#691])
- **fixed:** Make `nest("", service)` work and mean the same as `nest("/", service)` ([#691])
- **fixed:** Replace response code `301` with `308` for trailing slash redirects. Also deprecates
`Redirect::found` (`302`) in favor of `Redirect::temporary` (`307`) or `Redirect::to` (`303`).
This is to prevent clients from changing non-`GET` requests to `GET` requests ([#682])
[#691]: https://github.com/tokio-rs/axum/pull/691
[#682]: https://github.com/tokio-rs/axum/pull/682
* Add some debug assertions
As discussed in Discord its probably worth having debug assertions for
these two cases, should we accidentally break them in the future.
* remove assertion in `OriginalUri`
* Add `middleware::from_fn` for creating middleware from async fns
* More trait impls for `Next`
* Make `Next::run` consume `self`
* Use `.router_layer` in example, since middleware returns early
* Actually `Next` probably shouldn't impl `Clone` and `Service`
Has implications for backpressure and stuff
* Simplify `print-request-response` example
* Address review feedback
* add changelog link
- Support checking `FromRequest` bounds for extractors whose request body is something else than
`axum::body::Body`. Use `#[debug_handler(body = YourBodyType)]` to use a different request body
type ([#595])
[#595]: https://github.com/tokio-rs/axum/pull/595
* Provide more error in `Path` deserialization error
* Rename
* Add error kind for deserializing sequences
* Rename
* Fix wrong docs
* Rename `MissingRouteParams`
* Rename error to have more consistency
* Rename internal error
* Update changelog
* One last renaming, for now
* Add tests
* Tweak changelog a bit
* Move `IntoResponse` to axum-core
* Move `FromRequest` to axum-core
* some clean up
* Remove hyper dependency from axum-core
* Fix docs reference
* Use default
* Update changelog
* Remove mention of default type
* extra: Add `Cached` extractor
`Cached` wraps another extractor and caches its result in request
extensions.
* Use newtype to avoid overriding extensions of the same type
* Rename type param
* Fix `#[debug_handler]` for multiple extractors
It generated a function for each extractor to check the type but those
functions didn't have unique names.
Fixed by including all idents in the `arg: Extractor` token tree in the
name of the function generated. Not sure there is a simpler way to fix
it.
* just use a counter
* don't need visit feature anymore
* Make `axum-debug` handle more cases
* Only just trybuild tests on stable
* revert changes to hello-world example
* Remove a bit of duplication
* return error on generics
* address review feedback
* Support associated functions with receiver or returns `Self`
* fix indentation
Discovered while working on something else that we didn't properly
handle this:
```rust
let one = Router::new().route("/", get(|| async {}));
let two = Router::new().route("/", post(|| async {}));
let app = one.merge(two);
```
This introduces two new possible panics when constructing routers:
- If merging two routers that each have a fallback. Previously that left
side fallback would be silently discarded.
- If nesting a router that has a fallback. Previously it would be
silently discarded.
Overall this should make things more explicit and users shouldn't have
to worry "why isn't my fallback" working.
Fixes#488
* Add `axum-extra` crate
Empty for now but now we have a place to put stuff.
I'll make a PR for moving over https://github.com/davidpdrsn/axum-resource.
* remove `authors` field from `Cargo.toml`s
* Move `axum-handle-error-extract` into axum
With 0.4 underway we can now nuke `axum-handle-error-extract` and move
its code directly into axum.
So this replaces the old `HandleErrorLayer` with one that supports async
functions and extractors.
* changelog
* fix CI
* Empty crate
* basic setup
* Support `HEAD`
* Add remaining methods
* Impl Debug
* Add `MethodRouter::merge`
* WIP
* Support same route with different methods in different calls
* Update changelog
* Bring back `any` and `any_service`
* Address review feedback
* Clean up axum-debug crate
Mostly just brings the crate more in line with the rest of the crates in
the workspace.
I've also removed the axum-debug-macros crate since axum-debug only
contained one re-export from axum-debug-macros. So we didn't need two
crates. Can always bring the "backend" crate back if we need things in
axum-debug that aren't proc-macros.
* Just testing: This should make CI fail
* Misc CI clean up
* fix intentional breakage
* fix changelog
* Remove rustfmt config for now as it gives warnings on stable
* Fix paths
* Implement `IntoResponse` for `http::response::Parts`
Not sure there are many use cases for this but still thinks it makes to
provide since other crates can't.
* Use `Response::from_parts`
- **added:** Add `Router::route_layer` for applying middleware that
will only run on requests that match a route. This is useful for middleware
that return early, such as authorization ([#474])
[#474]: https://github.com/tokio-rs/axum/pull/474
This addresses something thats been bothering me for some time: Most middleware need to run regardless if the request matches a route or not. For example you don't wanna skip logging for unmatched requests.
However middleware such as authorization only make sense to run for matching requests. This previously wasn't possible to express and you'd have to manually apply the middleware to each handler. Consider this:
```rust
Router::new()
.route("/foo", get(|| async {}))
.layer(RequireAuthorizationLayer::bearer("password"));
```
Calling `GET /foo` with an invalid token would receive `401 Unauthorized` as expected however calling some unknown route like `GET /not-found` would also return `401 Unauthorized`. I think this is unexpected and have seen a few users ask questions about it.
It happened because the 404 you'd otherwise see is generated by a fallback service stored on `Router`. When adding a layer to the router the layer would also be applied to the fallback, which in the case of auth means the fallback would never be called for unauthorized requests.
I think what axum does today is the right default however I still think we should support this somehow. Especially since [`extractor_middleware`](https://docs.rs/axum/0.3.1/axum/extract/fn.extractor_middleware.html) is mainly useful for auth but it doesn't work great today due to this gotcha.
This PR proposes adding `Router::layer_on_matching_route` which only applies layers to routes, not the fallback, which fixes the issue. I'm not a big fan of the name `layer_on_matching_route`, would like something shorter, but I think it communicates the purpose decently.
The generics are a bit different since the request body used on the routes and the fallback must match, so layers that changes the request body type are not compatible with `layer_on_matching_route`. Such middleware are very rare so that should be fine.
* Move axum crate into workspace subfolder
Over time I imagine we're gonna have other crates in this repo that
provide utilities or integrations for axum. This prepares for that by
moving the main axum crate into its own folder.
The README situation is a bit annoying because we want `./README.md`
for viewing the repo on github but `axum/README.md` for crates.io. For
now I've just copy/pasted it and added CI step to make sure they're
identical.
* update changelog link
* Add licenses to all examples
* is this how you install `diff`?
* or maybe this is how?
* fix readme links
* like this?
* fix cargo-deny step
* Try making root readme a symlink
* remove compare readme step
not needed since readme in repo root is now a symlink
* Revert "Add licenses to all examples"
This reverts commit ab321b7fb9.
* Reorganize tests
This breaks up the large `crate::tests` module by moving some of the
tests into a place that makes more sense. For example tests of JSON
serialization are moved to the `crate::json` module. The remaining routing
tests have been moved to `crate::routing::tests`.
I generally prefer having tests close to the code they're testing. Makes
it easier to see how/if something is tested.
* Try pinning to older version of async-graphql
* Revert "Try pinning to older version of async-graphql"
This reverts commit 2e2cae7d12f5e433a16d6607497d587863f04384.
* don't test examples on 1.54 on CI
* move ci steps around a bit
- 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