Implement IntoResponse for Form (#1095)

This commit is contained in:
David Pedersen 2022-06-17 20:23:57 +02:00 committed by GitHub
parent 93251fa203
commit 2f64064650
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 62 additions and 26 deletions

View file

@ -9,9 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **added:** Support resolving host name via `Forwarded` header in `Host` - **added:** Support resolving host name via `Forwarded` header in `Host`
extractor ([#1078]) extractor ([#1078])
- **added:** Implement `IntoResponse` for `Form` ([#1095])
- **change:** axum's MSRV is now 1.56 ([#1098]) - **change:** axum's MSRV is now 1.56 ([#1098])
[#1078]: https://github.com/tokio-rs/axum/pull/1078 [#1078]: https://github.com/tokio-rs/axum/pull/1078
[#1095]: https://github.com/tokio-rs/axum/pull/1095
[#1098]: https://github.com/tokio-rs/axum/pull/1098 [#1098]: https://github.com/tokio-rs/axum/pull/1098
# 0.5.7 (08. June, 2022) # 0.5.7 (08. June, 2022)

View file

@ -39,11 +39,8 @@ pub use crate::Json;
pub use crate::Extension; pub use crate::Extension;
#[cfg(feature = "form")] #[cfg(feature = "form")]
mod form; #[doc(no_inline)]
pub use crate::form::Form;
#[cfg(feature = "form")]
#[doc(inline)]
pub use self::form::Form;
#[cfg(feature = "matched-path")] #[cfg(feature = "matched-path")]
mod matched_path; mod matched_path;

View file

@ -1,24 +1,23 @@
use super::{has_content_type, rejection::*, FromRequest, RequestParts};
use crate::body::{Bytes, HttpBody}; use crate::body::{Bytes, HttpBody};
use crate::extract::{has_content_type, rejection::*, FromRequest, RequestParts};
use crate::BoxError; use crate::BoxError;
use async_trait::async_trait; use async_trait::async_trait;
use http::Method; use axum_core::response::{IntoResponse, Response};
use http::header::CONTENT_TYPE;
use http::{Method, StatusCode};
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::Serialize;
use std::ops::Deref; use std::ops::Deref;
/// Extractor that deserializes `application/x-www-form-urlencoded` requests /// URL encoded extractor and response.
/// into some type.
/// ///
/// `T` is expected to implement [`serde::Deserialize`]. /// # As extractor
/// ///
/// # Example /// If used as an extractor `Form` will deserialize `application/x-www-form-urlencoded` request
/// bodies into some target type via [`serde::Deserialize`].
/// ///
/// ```rust,no_run /// ```rust
/// use axum::{ /// use axum::Form;
/// extract::Form,
/// routing::post,
/// Router,
/// };
/// use serde::Deserialize; /// use serde::Deserialize;
/// ///
/// #[derive(Deserialize)] /// #[derive(Deserialize)]
@ -27,19 +26,31 @@ use std::ops::Deref;
/// password: String, /// password: String,
/// } /// }
/// ///
/// async fn accept_form(form: Form<SignUp>) { /// async fn accept_form(Form(sign_up): Form<SignUp>) {
/// let sign_up: SignUp = form.0;
///
/// // ... /// // ...
/// } /// }
///
/// let app = Router::new().route("/sign_up", post(accept_form));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ``` /// ```
/// ///
/// Note that `Content-Type: multipart/form-data` requests are not supported. /// Note that `Content-Type: multipart/form-data` requests are not supported. Use [`Multipart`]
/// instead.
///
/// # As response
///
/// ```rust
/// use axum::Form;
/// use serde::Serialize;
///
/// #[derive(Serialize)]
/// struct Payload {
/// value: String,
/// }
///
/// async fn handler() -> Form<Payload> {
/// Form(Payload { value: "foo".to_owned() })
/// }
/// ```
///
/// [`Multipart`]: crate::extract::Multipart
#[cfg_attr(docsrs, doc(cfg(feature = "form")))] #[cfg_attr(docsrs, doc(cfg(feature = "form")))]
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default)]
pub struct Form<T>(pub T); pub struct Form<T>(pub T);
@ -74,6 +85,22 @@ where
} }
} }
impl<T> IntoResponse for Form<T>
where
T: Serialize,
{
fn into_response(self) -> Response {
match serde_urlencoded::to_string(&self.0) {
Ok(body) => (
[(CONTENT_TYPE, mime::APPLICATION_WWW_FORM_URLENCODED.as_ref())],
body,
)
.into_response(),
Err(err) => (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()).into_response(),
}
}
}
impl<T> Deref for Form<T> { impl<T> Deref for Form<T> {
type Target = T; type Target = T;

View file

@ -395,6 +395,8 @@
pub(crate) mod macros; pub(crate) mod macros;
mod extension; mod extension;
#[cfg(feature = "form")]
mod form;
#[cfg(feature = "json")] #[cfg(feature = "json")]
mod json; mod json;
#[cfg(feature = "headers")] #[cfg(feature = "headers")]
@ -434,5 +436,9 @@ pub use self::routing::Router;
#[cfg(feature = "headers")] #[cfg(feature = "headers")]
pub use self::typed_header::TypedHeader; pub use self::typed_header::TypedHeader;
#[doc(inline)]
#[cfg(feature = "headers")]
pub use form::Form;
#[doc(inline)] #[doc(inline)]
pub use axum_core::{BoxError, Error}; pub use axum_core::{BoxError, Error};

View file

@ -15,6 +15,10 @@ pub use crate::Json;
#[cfg(feature = "headers")] #[cfg(feature = "headers")]
pub use crate::TypedHeader; pub use crate::TypedHeader;
#[cfg(feature = "form")]
#[doc(no_inline)]
pub use crate::form::Form;
#[doc(no_inline)] #[doc(no_inline)]
pub use crate::Extension; pub use crate::Extension;