Skip to content

Commit 2f64064

Browse files
authored
Implement IntoResponse for Form (#1095)
1 parent 93251fa commit 2f64064

5 files changed

Lines changed: 61 additions & 25 deletions

File tree

axum/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
- **added:** Support resolving host name via `Forwarded` header in `Host`
1111
extractor ([#1078])
12+
- **added:** Implement `IntoResponse` for `Form` ([#1095])
1213
- **change:** axum's MSRV is now 1.56 ([#1098])
1314

1415
[#1078]: https://github.com/tokio-rs/axum/pull/1078
16+
[#1095]: https://github.com/tokio-rs/axum/pull/1095
1517
[#1098]: https://github.com/tokio-rs/axum/pull/1098
1618

1719
# 0.5.7 (08. June, 2022)

axum/src/extract/mod.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,8 @@ pub use crate::Json;
3939
pub use crate::Extension;
4040

4141
#[cfg(feature = "form")]
42-
mod form;
43-
44-
#[cfg(feature = "form")]
45-
#[doc(inline)]
46-
pub use self::form::Form;
42+
#[doc(no_inline)]
43+
pub use crate::form::Form;
4744

4845
#[cfg(feature = "matched-path")]
4946
mod matched_path;
Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,23 @@
1-
use super::{has_content_type, rejection::*, FromRequest, RequestParts};
21
use crate::body::{Bytes, HttpBody};
2+
use crate::extract::{has_content_type, rejection::*, FromRequest, RequestParts};
33
use crate::BoxError;
44
use async_trait::async_trait;
5-
use http::Method;
5+
use axum_core::response::{IntoResponse, Response};
6+
use http::header::CONTENT_TYPE;
7+
use http::{Method, StatusCode};
68
use serde::de::DeserializeOwned;
9+
use serde::Serialize;
710
use std::ops::Deref;
811

9-
/// Extractor that deserializes `application/x-www-form-urlencoded` requests
10-
/// into some type.
12+
/// URL encoded extractor and response.
1113
///
12-
/// `T` is expected to implement [`serde::Deserialize`].
14+
/// # As extractor
1315
///
14-
/// # Example
16+
/// If used as an extractor `Form` will deserialize `application/x-www-form-urlencoded` request
17+
/// bodies into some target type via [`serde::Deserialize`].
1518
///
16-
/// ```rust,no_run
17-
/// use axum::{
18-
/// extract::Form,
19-
/// routing::post,
20-
/// Router,
21-
/// };
19+
/// ```rust
20+
/// use axum::Form;
2221
/// use serde::Deserialize;
2322
///
2423
/// #[derive(Deserialize)]
@@ -27,19 +26,31 @@ use std::ops::Deref;
2726
/// password: String,
2827
/// }
2928
///
30-
/// async fn accept_form(form: Form<SignUp>) {
31-
/// let sign_up: SignUp = form.0;
32-
///
29+
/// async fn accept_form(Form(sign_up): Form<SignUp>) {
3330
/// // ...
3431
/// }
32+
/// ```
33+
///
34+
/// Note that `Content-Type: multipart/form-data` requests are not supported. Use [`Multipart`]
35+
/// instead.
36+
///
37+
/// # As response
38+
///
39+
/// ```rust
40+
/// use axum::Form;
41+
/// use serde::Serialize;
42+
///
43+
/// #[derive(Serialize)]
44+
/// struct Payload {
45+
/// value: String,
46+
/// }
3547
///
36-
/// let app = Router::new().route("/sign_up", post(accept_form));
37-
/// # async {
38-
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
39-
/// # };
48+
/// async fn handler() -> Form<Payload> {
49+
/// Form(Payload { value: "foo".to_owned() })
50+
/// }
4051
/// ```
4152
///
42-
/// Note that `Content-Type: multipart/form-data` requests are not supported.
53+
/// [`Multipart`]: crate::extract::Multipart
4354
#[cfg_attr(docsrs, doc(cfg(feature = "form")))]
4455
#[derive(Debug, Clone, Copy, Default)]
4556
pub struct Form<T>(pub T);
@@ -74,6 +85,22 @@ where
7485
}
7586
}
7687

88+
impl<T> IntoResponse for Form<T>
89+
where
90+
T: Serialize,
91+
{
92+
fn into_response(self) -> Response {
93+
match serde_urlencoded::to_string(&self.0) {
94+
Ok(body) => (
95+
[(CONTENT_TYPE, mime::APPLICATION_WWW_FORM_URLENCODED.as_ref())],
96+
body,
97+
)
98+
.into_response(),
99+
Err(err) => (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()).into_response(),
100+
}
101+
}
102+
}
103+
77104
impl<T> Deref for Form<T> {
78105
type Target = T;
79106

axum/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,8 @@
395395
pub(crate) mod macros;
396396

397397
mod extension;
398+
#[cfg(feature = "form")]
399+
mod form;
398400
#[cfg(feature = "json")]
399401
mod json;
400402
#[cfg(feature = "headers")]
@@ -434,5 +436,9 @@ pub use self::routing::Router;
434436
#[cfg(feature = "headers")]
435437
pub use self::typed_header::TypedHeader;
436438

439+
#[doc(inline)]
440+
#[cfg(feature = "headers")]
441+
pub use form::Form;
442+
437443
#[doc(inline)]
438444
pub use axum_core::{BoxError, Error};

axum/src/response/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ pub use crate::Json;
1515
#[cfg(feature = "headers")]
1616
pub use crate::TypedHeader;
1717

18+
#[cfg(feature = "form")]
19+
#[doc(no_inline)]
20+
pub use crate::form::Form;
21+
1822
#[doc(no_inline)]
1923
pub use crate::Extension;
2024

0 commit comments

Comments
 (0)