diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index 03a23dbb..6a517003 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -12,10 +12,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **fixed:** `sse::Event` is more strict about what field values it supports, disallowing any SSE events that break the specification (such as field values containing carriage returns) ([#599]) - **added:** `axum::AddExtension::layer` ([#607]) +- **fixed:** Make `Path` fail with `ExtensionsAlreadyExtracted` if another extractor (such as + `Request`) has previously taken the request extensions. Thus `PathRejection` now contains a + variant with `ExtensionsAlreadyExtracted`. This is not a breaking change since `PathRejection` is + marked as `#[non_exhaustive]` ([#619]) [#599]: https://github.com/tokio-rs/axum/pull/599 [#600]: https://github.com/tokio-rs/axum/pull/600 [#607]: https://github.com/tokio-rs/axum/pull/607 +[#619]: https://github.com/tokio-rs/axum/pull/619 # 0.4.2 (06. December, 2021) diff --git a/axum/src/extract/path/mod.rs b/axum/src/extract/path/mod.rs index 0760ac42..f5e12c8b 100644 --- a/axum/src/extract/path/mod.rs +++ b/axum/src/extract/path/mod.rs @@ -3,6 +3,7 @@ mod de; +use super::rejection::ExtensionsAlreadyExtracted; use crate::{ body::{boxed, Full}, extract::{rejection::*, FromRequest, RequestParts}, @@ -163,10 +164,11 @@ where type Rejection = PathRejection; async fn from_request(req: &mut RequestParts) -> Result { - let params = match req + let ext = req .extensions_mut() - .and_then(|ext| ext.get::>()) - { + .ok_or_else::(|| ExtensionsAlreadyExtracted::default().into())?; + + let params = match ext.get::>() { Some(Some(UrlParams(Ok(params)))) => Cow::Borrowed(params), Some(Some(UrlParams(Err(InvalidUtf8InPathParam { key })))) => { let err = PathDeserializationError { @@ -401,10 +403,10 @@ impl std::error::Error for FailedToDeserializePathParams {} #[cfg(test)] mod tests { - use http::StatusCode; - use super::*; use crate::{routing::get, test_helpers::*, Router}; + use http::{Request, StatusCode}; + use hyper::Body; use std::collections::HashMap; #[tokio::test] @@ -508,4 +510,15 @@ mod tests { let res = client.get("/foo").send().await; assert_eq!(res.status(), StatusCode::OK); } + + #[tokio::test] + async fn when_extensions_are_missing() { + let app = Router::new().route("/:key", get(|_: Request, _: Path| async {})); + + let client = TestClient::new(app); + + let res = client.get("/foo").send().await; + assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR); + assert_eq!(res.text().await, "Extensions taken by other extractor"); + } } diff --git a/axum/src/extract/rejection.rs b/axum/src/extract/rejection.rs index b449e777..82ca9fda 100644 --- a/axum/src/extract/rejection.rs +++ b/axum/src/extract/rejection.rs @@ -163,6 +163,7 @@ composite_rejection! { pub enum PathRejection { FailedToDeserializePathParams, MissingPathParams, + ExtensionsAlreadyExtracted, } }