Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove OptionalFromRequestParts impl for Query #3088

Merged
merged 3 commits into from
Dec 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions axum-extra/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@ and this project adheres to [Semantic Versioning].

# Unreleased

- **breaking:** `axum::extract::ws::Message` now uses `Bytes` in place of `Vec<u8>`,
jplatte marked this conversation as resolved.
Show resolved Hide resolved
and a new `Utf8Bytes` type in place of `String`, for its variants ([#3078])
- **changed:** Upgraded `tokio-tungstenite` to 0.26 ([#3078])
- **breaking:** Remove `OptionalFromRequestParts` impl for `Query` ([#3088])
- **changed:** Query/Form: Use `serde_path_to_error` to report fields that failed to parse ([#3081])

[#3078]: https://github.com/tokio-rs/axum/pull/3078
[#3081]: https://github.com/tokio-rs/axum/pull/3081
[#3088]: https://github.com/tokio-rs/axum/pull/3088

# 0.10.0

Expand Down
1 change: 0 additions & 1 deletion axum-extra/src/extract/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ pub use self::cookie::SignedCookieJar;
pub use self::form::{Form, FormRejection};

#[cfg(feature = "query")]
#[allow(deprecated)]
pub use self::query::OptionalQuery;
#[cfg(feature = "query")]
pub use self::query::{OptionalQueryRejection, Query, QueryRejection};
Expand Down
45 changes: 2 additions & 43 deletions axum-extra/src/extract/query.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use axum::{
extract::{FromRequestParts, OptionalFromRequestParts},
extract::FromRequestParts,
response::{IntoResponse, Response},
Error,
};
Expand All @@ -18,19 +18,6 @@ use std::fmt;
/// with the `multiple` attribute. Those values can be collected into a `Vec` or other sequential
/// container.
///
/// # `Option<Query<T>>` behavior
///
/// If `Query<T>` itself is used as an extractor and there is no query string in
/// the request URL, `T`'s `Deserialize` implementation is called on an empty
/// string instead.
///
/// You can avoid this by using `Option<Query<T>>`, which gives you `None` in
/// the case that there is no query string in the request URL.
///
/// Note that an empty query string is not the same as no query string, that is
/// `https://example.org/` and `https://example.org/?` are not treated the same
/// in this case.
///
/// # Example
///
/// ```rust,no_run
Expand Down Expand Up @@ -111,29 +98,6 @@ where
}
}

impl<T, S> OptionalFromRequestParts<S> for Query<T>
where
T: DeserializeOwned,
S: Send + Sync,
{
type Rejection = QueryRejection;

async fn from_request_parts(
parts: &mut Parts,
_state: &S,
) -> Result<Option<Self>, Self::Rejection> {
if let Some(query) = parts.uri.query() {
let deserializer =
serde_html_form::Deserializer::new(form_urlencoded::parse(query.as_bytes()));
let value = serde_path_to_error::deserialize(deserializer)
.map_err(|err| QueryRejection::FailedToDeserializeQueryString(Error::new(err)))?;
Ok(Some(Self(value)))
} else {
Ok(None)
}
}
}

axum_core::__impl_deref!(Query);

/// Rejection used for [`Query`].
Expand Down Expand Up @@ -181,8 +145,8 @@ impl std::error::Error for QueryRejection {
}

/// Extractor that deserializes query strings into `None` if no query parameters are present.
/// Otherwise behaviour is identical to [`Query`]
///
/// Otherwise behaviour is identical to [`Query`].
/// `T` is expected to implement [`serde::Deserialize`].
///
/// # Example
Expand Down Expand Up @@ -220,11 +184,9 @@ impl std::error::Error for QueryRejection {
///
/// [example]: https://github.com/tokio-rs/axum/blob/main/examples/query-params-with-empty-strings/src/main.rs
#[cfg_attr(docsrs, doc(cfg(feature = "query")))]
#[deprecated = "Use Option<Query<_>> instead"]
jplatte marked this conversation as resolved.
Show resolved Hide resolved
#[derive(Debug, Clone, Copy, Default)]
pub struct OptionalQuery<T>(pub Option<T>);

#[allow(deprecated)]
impl<T, S> FromRequestParts<S> for OptionalQuery<T>
where
T: DeserializeOwned,
Expand All @@ -246,7 +208,6 @@ where
}
}

#[allow(deprecated)]
impl<T> std::ops::Deref for OptionalQuery<T> {
type Target = Option<T>;

Expand All @@ -256,7 +217,6 @@ impl<T> std::ops::Deref for OptionalQuery<T> {
}
}

#[allow(deprecated)]
impl<T> std::ops::DerefMut for OptionalQuery<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
Expand Down Expand Up @@ -304,7 +264,6 @@ impl std::error::Error for OptionalQueryRejection {
}

#[cfg(test)]
#[allow(deprecated)]
mod tests {
use super::*;
use crate::test_helpers::*;
Expand Down
6 changes: 6 additions & 0 deletions axum/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

# Unreleased

- **breaking:** `axum::extract::ws::Message` now uses `Bytes` in place of `Vec<u8>`,
and a new `Utf8Bytes` type in place of `String`, for its variants ([#3078])
- **breaking:** Remove `OptionalFromRequestParts` impl for `Query` ([#3088])
- **changed:** Upgraded `tokio-tungstenite` to 0.26 ([#3078])
- **changed:** Query/Form: Use `serde_path_to_error` to report fields that failed to parse ([#3081])

[#3078]: https://github.com/tokio-rs/axum/pull/3078
[#3081]: https://github.com/tokio-rs/axum/pull/3081
[#3088]: https://github.com/tokio-rs/axum/pull/3088

# 0.8.0

Expand Down
10 changes: 1 addition & 9 deletions axum/src/docs/extract.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,18 +108,10 @@ struct Pagination {
per_page: usize,
jplatte marked this conversation as resolved.
Show resolved Hide resolved
}
impl Default for Pagination {
fn default() -> Self {
Self { page: 1, per_page: 30 }
}
}
async fn get_user_things(
Path(user_id): Path<Uuid>,
pagination: Option<Query<Pagination>>,
Query(pagination): Query<Pagination>,
) {
let Query(pagination) = pagination.unwrap_or_default();
// ...
}
# let _: Router = app;
Expand Down
38 changes: 1 addition & 37 deletions axum/src/extract/query.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,11 @@
use super::{rejection::*, FromRequestParts, OptionalFromRequestParts};
use super::{rejection::*, FromRequestParts};
use http::{request::Parts, Uri};
use serde::de::DeserializeOwned;

/// Extractor that deserializes query strings into some type.
///
/// `T` is expected to implement [`serde::Deserialize`].
///
/// # `Option<Query<T>>` behavior
///
/// If `Query<T>` itself is used as an extractor and there is no query string in
/// the request URL, `T`'s `Deserialize` implementation is called on an empty
/// string instead.
///
/// You can avoid this by using `Option<Query<T>>`, which gives you `None` in
/// the case that there is no query string in the request URL.
///
/// Note that an empty query string is not the same as no query string, that is
/// `https://example.org/` and `https://example.org/?` are not treated the same
/// in this case.
///
/// # Examples
///
/// ```rust,no_run
Expand Down Expand Up @@ -75,29 +62,6 @@ where
}
}

impl<T, S> OptionalFromRequestParts<S> for Query<T>
where
T: DeserializeOwned,
S: Send + Sync,
{
type Rejection = QueryRejection;

async fn from_request_parts(
parts: &mut Parts,
_state: &S,
) -> Result<Option<Self>, Self::Rejection> {
if let Some(query) = parts.uri.query() {
let deserializer =
serde_urlencoded::Deserializer::new(form_urlencoded::parse(query.as_bytes()));
let value = serde_path_to_error::deserialize(deserializer)
.map_err(FailedToDeserializeQueryString::from_err)?;
Ok(Some(Self(value)))
} else {
Ok(None)
}
}
}

impl<T> Query<T>
where
T: DeserializeOwned,
Expand Down