mirror of
https://github.com/tokio-rs/axum.git
synced 2025-01-11 12:31:25 +01:00
Panic instead of silently discarding fallbacks (#529)
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
This commit is contained in:
parent
badbb14cce
commit
1d94d75c57
7 changed files with 38 additions and 67 deletions
|
@ -28,10 +28,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- **breaking:** The `Handler<B, T>` trait is now defined as `Handler<T, B =
|
||||
Body>`. That is the type parameters have been swapped and `B` defaults to
|
||||
`axum::body::Body` ([#527])
|
||||
- **breaking:** `Router::merge` will panic if both routers have fallbacks.
|
||||
Previously the left side fallback would be silently discarded ([#529])
|
||||
- **breaking:** `Router::nest` will panic if the nested router has a fallback.
|
||||
Previously it would be silently discarded ([#529])
|
||||
- Update WebSockets to use tokio-tungstenite 0.16 ([#525])
|
||||
|
||||
[#525]: https://github.com/tokio-rs/axum/pull/525
|
||||
[#527]: https://github.com/tokio-rs/axum/pull/527
|
||||
[#529]: https://github.com/tokio-rs/axum/pull/529
|
||||
[#534]: https://github.com/tokio-rs/axum/pull/534
|
||||
|
||||
# 0.3.3 (13. November, 2021)
|
||||
|
|
|
@ -26,45 +26,3 @@ async fn fallback(uri: Uri) -> impl IntoResponse {
|
|||
Fallbacks only apply to routes that aren't matched by anything in the
|
||||
router. If a handler is matched by a request but returns 404 the
|
||||
fallback is not called.
|
||||
|
||||
## When used with `Router::merge`
|
||||
|
||||
If a router with a fallback is merged with another router that also has
|
||||
a fallback the fallback of the second router takes precedence:
|
||||
|
||||
```rust
|
||||
use axum::{
|
||||
Router,
|
||||
routing::get,
|
||||
handler::Handler,
|
||||
response::IntoResponse,
|
||||
http::{StatusCode, Uri},
|
||||
};
|
||||
|
||||
let one = Router::new()
|
||||
.route("/one", get(|| async {}))
|
||||
.fallback(fallback_one.into_service());
|
||||
|
||||
let two = Router::new()
|
||||
.route("/two", get(|| async {}))
|
||||
.fallback(fallback_two.into_service());
|
||||
|
||||
let app = one.merge(two);
|
||||
|
||||
async fn fallback_one() -> impl IntoResponse {}
|
||||
async fn fallback_two() -> impl IntoResponse {}
|
||||
|
||||
// the fallback for `app` is `fallback_two`
|
||||
# async {
|
||||
# hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
# };
|
||||
```
|
||||
|
||||
If only one of the routers have a fallback that will be used in the
|
||||
merged router.
|
||||
|
||||
## When used with `Router::nest`
|
||||
|
||||
If a router with a fallback is nested inside another router the fallback
|
||||
of the nested router will be discarded and not used. This is such that
|
||||
the outer router's fallback takes precedence.
|
||||
|
|
|
@ -36,3 +36,8 @@ let app = Router::new()
|
|||
# hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
# };
|
||||
```
|
||||
|
||||
## Panics
|
||||
|
||||
- If two routers that each have a [fallback](Router::fallback) are merged. This
|
||||
is because `Router` only allows a single fallback.
|
||||
|
|
|
@ -121,5 +121,7 @@ let app = Router::new()
|
|||
for more details.
|
||||
- If the route contains a wildcard (`*`).
|
||||
- If `path` is empty.
|
||||
- If the nested router has a [fallback](Router::fallback). This is because
|
||||
`Router` only allows a single fallback.
|
||||
|
||||
[`OriginalUri`]: crate::extract::OriginalUri
|
||||
|
|
|
@ -189,14 +189,17 @@ where
|
|||
let Router {
|
||||
mut routes,
|
||||
node,
|
||||
// discard the fallback of the nested router
|
||||
fallback: _,
|
||||
fallback,
|
||||
// nesting a router that has something nested at root
|
||||
// doesn't mean something is nested at root in _this_ router
|
||||
// thus we don't need to propagate that
|
||||
nested_at_root: _,
|
||||
} = router;
|
||||
|
||||
if let Fallback::Custom(_) = fallback {
|
||||
panic!("Cannot nest `Router`s that has a fallback");
|
||||
}
|
||||
|
||||
for (id, nested_path) in node.route_id_to_path {
|
||||
let route = routes.remove(&id).unwrap();
|
||||
let full_path = if &*nested_path == "/" {
|
||||
|
@ -253,7 +256,9 @@ where
|
|||
(Fallback::Default(_), pick @ Fallback::Default(_)) => pick,
|
||||
(Fallback::Default(_), pick @ Fallback::Custom(_)) => pick,
|
||||
(pick @ Fallback::Custom(_), Fallback::Default(_)) => pick,
|
||||
(Fallback::Custom(_), pick @ Fallback::Custom(_)) => pick,
|
||||
(Fallback::Custom(_), Fallback::Custom(_)) => {
|
||||
panic!("Cannot merge two `Router`s that both have a fallback")
|
||||
}
|
||||
};
|
||||
|
||||
self.nested_at_root = self.nested_at_root || nested_at_root;
|
||||
|
|
|
@ -49,25 +49,3 @@ async fn or() {
|
|||
assert_eq!(res.status(), StatusCode::OK);
|
||||
assert_eq!(res.text().await, "fallback");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn fallback_on_or() {
|
||||
let one = Router::new()
|
||||
.route("/one", get(|| async {}))
|
||||
.fallback((|| async { "fallback one" }).into_service());
|
||||
|
||||
let two = Router::new()
|
||||
.route("/two", get(|| async {}))
|
||||
.fallback((|| async { "fallback two" }).into_service());
|
||||
|
||||
let app = one.merge(two);
|
||||
|
||||
let client = TestClient::new(app);
|
||||
|
||||
assert_eq!(client.get("/one").send().await.status(), StatusCode::OK);
|
||||
assert_eq!(client.get("/two").send().await.status(), StatusCode::OK);
|
||||
|
||||
let res = client.get("/does-not-exist").send().await;
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
assert_eq!(res.text().await, "fallback two");
|
||||
}
|
||||
|
|
|
@ -566,3 +566,21 @@ async fn different_methods_added_in_different_routes_deeply_nested() {
|
|||
let body = res.text().await;
|
||||
assert_eq!(body, "POST");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[should_panic(expected = "Cannot merge two `Router`s that both have a fallback")]
|
||||
async fn merging_routers_with_fallbacks_panics() {
|
||||
async fn fallback() {}
|
||||
let one = Router::new().fallback(fallback.into_service());
|
||||
let two = Router::new().fallback(fallback.into_service());
|
||||
TestClient::new(one.merge(two));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[should_panic(expected = "Cannot nest `Router`s that has a fallback")]
|
||||
async fn nesting_router_with_fallbacks_panics() {
|
||||
async fn fallback() {}
|
||||
let one = Router::new().fallback(fallback.into_service());
|
||||
let app = Router::new().nest("/", one);
|
||||
TestClient::new(app);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue