Fix Uri extractor not being the full URI if using nest (#156)

This commit is contained in:
David Pedersen 2021-08-07 22:07:50 +02:00 committed by GitHub
parent a6b3e09827
commit c570fb2d52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 49 additions and 2 deletions

View File

@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Make `FromRequest` default to being generic over `axum::body::Body` ([#146](https://github.com/tokio-rs/axum/pull/146)) - Make `FromRequest` default to being generic over `axum::body::Body` ([#146](https://github.com/tokio-rs/axum/pull/146))
- Implement `std::error::Error` for all rejections ([#153](https://github.com/tokio-rs/axum/pull/153)) - Implement `std::error::Error` for all rejections ([#153](https://github.com/tokio-rs/axum/pull/153))
- Fix `Uri` extractor not being the full URI if using `nest` ([#156](https://github.com/tokio-rs/axum/pull/156))
## Breaking changes ## Breaking changes

View File

@ -244,7 +244,7 @@
//! //!
//! [`body::Body`]: crate::body::Body //! [`body::Body`]: crate::body::Body
use crate::response::IntoResponse; use crate::{response::IntoResponse, routing::OriginalUri};
use async_trait::async_trait; use async_trait::async_trait;
use http::{header, Extensions, HeaderMap, Method, Request, Uri, Version}; use http::{header, Extensions, HeaderMap, Method, Request, Uri, Version};
use rejection::*; use rejection::*;
@ -377,7 +377,7 @@ impl<B> RequestParts<B> {
let ( let (
http::request::Parts { http::request::Parts {
method, method,
uri, mut uri,
version, version,
headers, headers,
extensions, extensions,
@ -386,6 +386,10 @@ impl<B> RequestParts<B> {
body, body,
) = req.into_parts(); ) = req.into_parts();
if let Some(original_uri) = extensions.get::<OriginalUri>() {
uri = original_uri.0.clone();
};
RequestParts { RequestParts {
method, method,
uri, uri,

View File

@ -907,6 +907,11 @@ where
} }
fn call(&mut self, mut req: Request<B>) -> Self::Future { fn call(&mut self, mut req: Request<B>) -> Self::Future {
if req.extensions().get::<OriginalUri>().is_none() {
let original_uri = OriginalUri(req.uri().clone());
req.extensions_mut().insert(original_uri);
}
if let Some((prefix, captures)) = self.pattern.prefix_match(req.uri().path()) { if let Some((prefix, captures)) = self.pattern.prefix_match(req.uri().path()) {
let without_prefix = strip_prefix(req.uri(), prefix); let without_prefix = strip_prefix(req.uri(), prefix);
*req.uri_mut() = without_prefix; *req.uri_mut() = without_prefix;
@ -921,6 +926,11 @@ where
} }
} }
/// `Nested` changes the incoming requests URI. This will be saved as an
/// extension so extractors can still access the original URI.
#[derive(Clone)]
pub(crate) struct OriginalUri(pub(crate) Uri);
fn strip_prefix(uri: &Uri, prefix: &str) -> Uri { fn strip_prefix(uri: &Uri, prefix: &str) -> Uri {
let path_and_query = if let Some(path_and_query) = uri.path_and_query() { let path_and_query = if let Some(path_and_query) = uri.path_and_query() {
let new_path = if let Some(path) = path_and_query.path().strip_prefix(prefix) { let new_path = if let Some(path) = path_and_query.path().strip_prefix(prefix) {

View File

@ -126,3 +126,35 @@ async fn nesting_at_root() {
assert_eq!(res.status(), StatusCode::OK); assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.text().await.unwrap(), "/foo/bar"); assert_eq!(res.text().await.unwrap(), "/foo/bar");
} }
#[tokio::test]
async fn nested_url_extractor() {
let app = nest(
"/foo",
nest(
"/bar",
route("/baz", get(|uri: Uri| async move { uri.to_string() })).route(
"/qux",
get(|req: Request<Body>| async move { req.uri().to_string() }),
),
),
);
let addr = run_in_background(app).await;
let client = reqwest::Client::new();
let res = client
.get(format!("http://{}/foo/bar/baz", addr))
.send()
.await
.unwrap();
assert_eq!(res.text().await.unwrap(), "/foo/bar/baz");
let res = client
.get(format!("http://{}/foo/bar/qux", addr))
.send()
.await
.unwrap();
assert_eq!(res.text().await.unwrap(), "/foo/bar/qux");
}