diff --git a/axum-extra/src/extract/cookie.rs b/axum-extra/src/extract/cookie.rs index b638da49..e825c60b 100644 --- a/axum-extra/src/extract/cookie.rs +++ b/axum-extra/src/extract/cookie.rs @@ -45,7 +45,7 @@ pub use cookie_lib::{Cookie, Key}; /// // the updated jar must be returned for the changes /// // to be included in the response /// jar.add(Cookie::new("session_id", session_id)), -/// Redirect::to("/me".parse().unwrap()), +/// Redirect::to("/me"), /// )) /// } else { /// Err(StatusCode::UNAUTHORIZED) @@ -214,7 +214,7 @@ impl IntoResponse for CookieJar { /// // the updated jar must be returned for the changes /// // to be included in the response /// jar.add(Cookie::new("session_id", session_id)), -/// Redirect::to("/me".parse().unwrap()), +/// Redirect::to("/me"), /// )) /// } else { /// Err(StatusCode::UNAUTHORIZED) diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index 8ffdbe6f..628b3ef8 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -86,6 +86,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **fixed:** Fixed several routing bugs related to nested "opaque" tower services (i.e. non-`Router` services) ([#841] and [#842]) - **changed:** Update to tokio-tungstenite 0.17 ([#791]) +- **breaking:** `Redirect::{to, temporary, permanent}` now accept `&str` instead + of `Uri` ([#889]) [#644]: https://github.com/tokio-rs/axum/pull/644 [#665]: https://github.com/tokio-rs/axum/pull/665 @@ -108,6 +110,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#841]: https://github.com/tokio-rs/axum/pull/841 [#842]: https://github.com/tokio-rs/axum/pull/842 [#879]: https://github.com/tokio-rs/axum/pull/879 +[#889]: https://github.com/tokio-rs/axum/pull/889 # 0.4.4 (13. January, 2022) diff --git a/axum/src/response/redirect.rs b/axum/src/response/redirect.rs index 77999378..a994db7b 100644 --- a/axum/src/response/redirect.rs +++ b/axum/src/response/redirect.rs @@ -1,5 +1,5 @@ use axum_core::response::{IntoResponse, Response}; -use http::{header::LOCATION, HeaderValue, StatusCode, Uri}; +use http::{header::LOCATION, HeaderValue, StatusCode}; use std::convert::TryFrom; /// Response that redirects the request to another location. @@ -14,7 +14,7 @@ use std::convert::TryFrom; /// }; /// /// let app = Router::new() -/// .route("/old", get(|| async { Redirect::permanent("/new".parse().unwrap()) })) +/// .route("/old", get(|| async { Redirect::permanent("/new") })) /// .route("/new", get(|| async { "Hello!" })); /// # async { /// # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); @@ -40,7 +40,7 @@ impl Redirect { /// If `uri` isn't a valid [`HeaderValue`]. /// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/303 - pub fn to(uri: Uri) -> Self { + pub fn to(uri: &str) -> Self { Self::with_status_code(StatusCode::SEE_OTHER, uri) } @@ -54,7 +54,7 @@ impl Redirect { /// If `uri` isn't a valid [`HeaderValue`]. /// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/307 - pub fn temporary(uri: Uri) -> Self { + pub fn temporary(uri: &str) -> Self { Self::with_status_code(StatusCode::TEMPORARY_REDIRECT, uri) } @@ -65,7 +65,7 @@ impl Redirect { /// If `uri` isn't a valid [`HeaderValue`]. /// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/308 - pub fn permanent(uri: Uri) -> Self { + pub fn permanent(uri: &str) -> Self { Self::with_status_code(StatusCode::PERMANENT_REDIRECT, uri) } @@ -73,7 +73,7 @@ impl Redirect { // use the `Location` header, namely `304 Not Modified`. // // We're open to adding more constructors upon request, if they make sense :) - fn with_status_code(status_code: StatusCode, uri: Uri) -> Self { + fn with_status_code(status_code: StatusCode, uri: &str) -> Self { assert!( status_code.is_redirection(), "not a redirection status code" @@ -81,8 +81,7 @@ impl Redirect { Self { status_code, - location: HeaderValue::try_from(uri.to_string()) - .expect("URI isn't a valid header value"), + location: HeaderValue::try_from(uri).expect("URI isn't a valid header value"), } } } diff --git a/axum/src/routing/mod.rs b/axum/src/routing/mod.rs index 8f38f0a1..3165422b 100644 --- a/axum/src/routing/mod.rs +++ b/axum/src/routing/mod.rs @@ -9,7 +9,7 @@ use crate::{ util::try_downcast, BoxError, }; -use http::{Request, Uri}; +use http::Request; use matchit::MatchError; use std::{ borrow::Cow, @@ -503,10 +503,10 @@ where match self.node.at(&path) { Ok(match_) => self.call_route(match_, req), Err(MatchError::MissingTrailingSlash) => RouteFuture::from_response( - Redirect::permanent(with_path(req.uri(), &format!("{}/", path))).into_response(), + Redirect::permanent(&format!("{}/", req.uri().to_string())).into_response(), ), Err(MatchError::ExtraTrailingSlash) => RouteFuture::from_response( - Redirect::permanent(with_path(req.uri(), path.strip_suffix('/').unwrap())) + Redirect::permanent(&req.uri().to_string().strip_suffix('/').unwrap()) .into_response(), ), Err(MatchError::NotFound) => match &self.fallback { @@ -517,35 +517,6 @@ where } } -fn with_path(uri: &Uri, new_path: &str) -> Uri { - let path_and_query = if let Some(path_and_query) = uri.path_and_query() { - let new_path = if new_path.starts_with('/') { - Cow::Borrowed(new_path) - } else { - Cow::Owned(format!("/{}", new_path)) - }; - - if let Some(query) = path_and_query.query() { - Some( - format!("{}?{}", new_path, query) - .parse::<http::uri::PathAndQuery>() - .unwrap(), - ) - } else { - Some(new_path.parse().unwrap()) - } - } else { - None - }; - - let mut parts = http::uri::Parts::default(); - parts.scheme = uri.scheme().cloned(); - parts.authority = uri.authority().cloned(); - parts.path_and_query = path_and_query; - - Uri::from_parts(parts).unwrap() -} - /// Wrapper around `matchit::Router` that supports merging two `Router`s. #[derive(Clone, Default)] struct Node { diff --git a/examples/oauth/src/main.rs b/examples/oauth/src/main.rs index d4dfaf35..6357a7fc 100644 --- a/examples/oauth/src/main.rs +++ b/examples/oauth/src/main.rs @@ -120,7 +120,7 @@ async fn discord_auth(Extension(client): Extension<BasicClient>) -> impl IntoRes .url(); // Redirect to Discord's oauth service - Redirect::to(auth_url.to_string().parse().unwrap()) + Redirect::to(&auth_url.to_string()) } // Valid user session required. If there is none, redirect to the auth page @@ -139,12 +139,12 @@ async fn logout( let session = match store.load_session(cookie.to_string()).await.unwrap() { Some(s) => s, // No session active, just redirect - None => return Redirect::to("/".parse().unwrap()), + None => return Redirect::to("/"), }; store.destroy_session(session).await.unwrap(); - Redirect::to("/".parse().unwrap()) + Redirect::to("/") } #[derive(Debug, Deserialize)] @@ -193,14 +193,14 @@ async fn login_authorized( let mut headers = HeaderMap::new(); headers.insert(SET_COOKIE, cookie.parse().unwrap()); - (headers, Redirect::to("/".parse().unwrap())) + (headers, Redirect::to("/")) } struct AuthRedirect; impl IntoResponse for AuthRedirect { fn into_response(self) -> Response { - Redirect::temporary("/auth/discord".parse().unwrap()).into_response() + Redirect::temporary("/auth/discord").into_response() } }