mirror of
https://github.com/tokio-rs/axum.git
synced 2024-12-28 07:20:12 +01:00
Remove OptionalFromRequestParts impl for Query (#3088)
This commit is contained in:
parent
1bc694878b
commit
3bb12abc8d
6 changed files with 12 additions and 94 deletions
|
@ -7,13 +7,11 @@ and this project adheres to [Semantic Versioning].
|
||||||
|
|
||||||
# Unreleased
|
# Unreleased
|
||||||
|
|
||||||
- **breaking:** `axum::extract::ws::Message` now uses `Bytes` in place of `Vec<u8>`,
|
- **breaking:** Remove `OptionalFromRequestParts` impl for `Query` ([#3088])
|
||||||
and a new `Utf8Bytes` type in place of `String`, for its variants ([#3078])
|
|
||||||
- **changed:** Upgraded `tokio-tungstenite` to 0.26 ([#3078])
|
|
||||||
- **changed:** Query/Form: Use `serde_path_to_error` to report fields that failed to parse ([#3081])
|
- **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
|
[#3081]: https://github.com/tokio-rs/axum/pull/3081
|
||||||
|
[#3088]: https://github.com/tokio-rs/axum/pull/3088
|
||||||
|
|
||||||
# 0.10.0
|
# 0.10.0
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,6 @@ pub use self::cookie::SignedCookieJar;
|
||||||
pub use self::form::{Form, FormRejection};
|
pub use self::form::{Form, FormRejection};
|
||||||
|
|
||||||
#[cfg(feature = "query")]
|
#[cfg(feature = "query")]
|
||||||
#[allow(deprecated)]
|
|
||||||
pub use self::query::OptionalQuery;
|
pub use self::query::OptionalQuery;
|
||||||
#[cfg(feature = "query")]
|
#[cfg(feature = "query")]
|
||||||
pub use self::query::{OptionalQueryRejection, Query, QueryRejection};
|
pub use self::query::{OptionalQueryRejection, Query, QueryRejection};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::{FromRequestParts, OptionalFromRequestParts},
|
extract::FromRequestParts,
|
||||||
response::{IntoResponse, Response},
|
response::{IntoResponse, Response},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
@ -18,19 +18,6 @@ use std::fmt;
|
||||||
/// with the `multiple` attribute. Those values can be collected into a `Vec` or other sequential
|
/// with the `multiple` attribute. Those values can be collected into a `Vec` or other sequential
|
||||||
/// container.
|
/// 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
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
|
@ -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);
|
axum_core::__impl_deref!(Query);
|
||||||
|
|
||||||
/// Rejection used for [`Query`].
|
/// Rejection used for [`Query`].
|
||||||
|
@ -181,8 +145,8 @@ impl std::error::Error for QueryRejection {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extractor that deserializes query strings into `None` if no query parameters are present.
|
/// 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`].
|
/// `T` is expected to implement [`serde::Deserialize`].
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
|
@ -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
|
/// [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")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "query")))]
|
||||||
#[deprecated = "Use Option<Query<_>> instead"]
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
pub struct OptionalQuery<T>(pub Option<T>);
|
pub struct OptionalQuery<T>(pub Option<T>);
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
impl<T, S> FromRequestParts<S> for OptionalQuery<T>
|
impl<T, S> FromRequestParts<S> for OptionalQuery<T>
|
||||||
where
|
where
|
||||||
T: DeserializeOwned,
|
T: DeserializeOwned,
|
||||||
|
@ -246,7 +208,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
impl<T> std::ops::Deref for OptionalQuery<T> {
|
impl<T> std::ops::Deref for OptionalQuery<T> {
|
||||||
type Target = Option<T>;
|
type Target = Option<T>;
|
||||||
|
|
||||||
|
@ -256,7 +217,6 @@ impl<T> std::ops::Deref for OptionalQuery<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
impl<T> std::ops::DerefMut for OptionalQuery<T> {
|
impl<T> std::ops::DerefMut for OptionalQuery<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
@ -304,7 +264,6 @@ impl std::error::Error for OptionalQueryRejection {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(deprecated)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::test_helpers::*;
|
use crate::test_helpers::*;
|
||||||
|
|
|
@ -7,9 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
# Unreleased
|
# 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])
|
- **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
|
[#3081]: https://github.com/tokio-rs/axum/pull/3081
|
||||||
|
[#3088]: https://github.com/tokio-rs/axum/pull/3088
|
||||||
|
|
||||||
# 0.8.0
|
# 0.8.0
|
||||||
|
|
||||||
|
|
|
@ -108,18 +108,10 @@ struct Pagination {
|
||||||
per_page: usize,
|
per_page: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Pagination {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self { page: 1, per_page: 30 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_user_things(
|
async fn get_user_things(
|
||||||
Path(user_id): Path<Uuid>,
|
Path(user_id): Path<Uuid>,
|
||||||
pagination: Option<Query<Pagination>>,
|
Query(pagination): Query<Pagination>,
|
||||||
) {
|
) {
|
||||||
let Query(pagination) = pagination.unwrap_or_default();
|
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
# let _: Router = app;
|
# let _: Router = app;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use super::{rejection::*, FromRequestParts, OptionalFromRequestParts};
|
use super::{rejection::*, FromRequestParts};
|
||||||
use http::{request::Parts, Uri};
|
use http::{request::Parts, Uri};
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
|
|
||||||
|
@ -6,19 +6,6 @@ use serde::de::DeserializeOwned;
|
||||||
///
|
///
|
||||||
/// `T` is expected to implement [`serde::Deserialize`].
|
/// `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
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
|
@ -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>
|
impl<T> Query<T>
|
||||||
where
|
where
|
||||||
T: DeserializeOwned,
|
T: DeserializeOwned,
|
||||||
|
|
Loading…
Reference in a new issue