mirror of
https://github.com/actix/actix-web.git
synced 2024-12-30 12:00:38 +00:00
impl Responder for Form
This commit is contained in:
parent
c96068e78d
commit
8f48ed2597
1 changed files with 99 additions and 16 deletions
|
@ -3,20 +3,29 @@
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::{fmt, ops};
|
use std::{fmt, ops};
|
||||||
|
|
||||||
use actix_http::{Error, HttpMessage, Payload};
|
use actix_http::{Error, HttpMessage, Payload, Response};
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use encoding_rs::{Encoding, UTF_8};
|
use encoding_rs::{Encoding, UTF_8};
|
||||||
use futures::{Future, Poll, Stream};
|
use futures::{Future, Poll, Stream};
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::dev::Decompress;
|
use crate::dev::Decompress;
|
||||||
use crate::error::UrlencodedError;
|
use crate::error::UrlencodedError;
|
||||||
use crate::extract::FromRequest;
|
use crate::extract::FromRequest;
|
||||||
use crate::http::header::CONTENT_LENGTH;
|
use crate::http::{
|
||||||
|
header::{ContentType, CONTENT_LENGTH},
|
||||||
|
StatusCode,
|
||||||
|
};
|
||||||
use crate::request::HttpRequest;
|
use crate::request::HttpRequest;
|
||||||
|
use crate::responder::Responder;
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
/// Form data helper (`application/x-www-form-urlencoded`)
|
||||||
/// Extract typed information from the request's body.
|
///
|
||||||
|
/// Can be use to extract url-encoded data from the request body,
|
||||||
|
/// or send url-encoded data as the response.
|
||||||
|
///
|
||||||
|
/// ## Extract
|
||||||
///
|
///
|
||||||
/// To extract typed information from request's body, the type `T` must
|
/// To extract typed information from request's body, the type `T` must
|
||||||
/// implement the `Deserialize` trait from *serde*.
|
/// implement the `Deserialize` trait from *serde*.
|
||||||
|
@ -24,8 +33,7 @@ use crate::request::HttpRequest;
|
||||||
/// [**FormConfig**](struct.FormConfig.html) allows to configure extraction
|
/// [**FormConfig**](struct.FormConfig.html) allows to configure extraction
|
||||||
/// process.
|
/// process.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// ### Example
|
||||||
///
|
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # extern crate actix_web;
|
/// # extern crate actix_web;
|
||||||
/// #[macro_use] extern crate serde_derive;
|
/// #[macro_use] extern crate serde_derive;
|
||||||
|
@ -44,6 +52,36 @@ use crate::request::HttpRequest;
|
||||||
/// }
|
/// }
|
||||||
/// # fn main() {}
|
/// # fn main() {}
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Respond
|
||||||
|
///
|
||||||
|
/// The `Form` type also allows you to respond with well-formed url-encoded data:
|
||||||
|
/// simply return a value of type Form<T> where T is the type to be url-encoded.
|
||||||
|
/// The type must implement `serde::Serialize`;
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```rust
|
||||||
|
/// # #[macro_use] extern crate serde_derive;
|
||||||
|
/// # use actix_web::*;
|
||||||
|
/// #
|
||||||
|
/// #[derive(Serialize)]
|
||||||
|
/// struct SomeForm {
|
||||||
|
/// name: String,
|
||||||
|
/// age: u8
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // Will return a 200 response with header
|
||||||
|
/// // `Content-Type: application/x-www-form-urlencoded`
|
||||||
|
/// // and body "name=actix&age=123"
|
||||||
|
/// fn index() -> web::Form<SomeForm> {
|
||||||
|
/// web::Form(SomeForm {
|
||||||
|
/// name: "actix".into(),
|
||||||
|
/// age: 123
|
||||||
|
/// })
|
||||||
|
/// }
|
||||||
|
/// # fn main() {}
|
||||||
|
/// ```
|
||||||
|
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct Form<T>(pub T);
|
pub struct Form<T>(pub T);
|
||||||
|
|
||||||
impl<T> Form<T> {
|
impl<T> Form<T> {
|
||||||
|
@ -110,6 +148,22 @@ impl<T: fmt::Display> fmt::Display for Form<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Serialize> Responder for Form<T> {
|
||||||
|
type Error = Error;
|
||||||
|
type Future = Result<Response, Error>;
|
||||||
|
|
||||||
|
fn respond_to(self, _: &HttpRequest) -> Self::Future {
|
||||||
|
let body = match serde_urlencoded::to_string(&self.0) {
|
||||||
|
Ok(body) => body,
|
||||||
|
Err(e) => return Err(e.into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Response::build(StatusCode::OK)
|
||||||
|
.set(ContentType::form_url_encoded())
|
||||||
|
.body(body))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Form extractor configuration
|
/// Form extractor configuration
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
|
@ -304,15 +358,16 @@ where
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use serde::Deserialize;
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::http::header::CONTENT_TYPE;
|
use crate::http::header::{HeaderValue, CONTENT_TYPE};
|
||||||
use crate::test::{block_on, TestRequest};
|
use crate::test::{block_on, TestRequest};
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, PartialEq)]
|
#[derive(Deserialize, Serialize, Debug, PartialEq)]
|
||||||
struct Info {
|
struct Info {
|
||||||
hello: String,
|
hello: String,
|
||||||
|
counter: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -320,11 +375,17 @@ mod tests {
|
||||||
let (req, mut pl) =
|
let (req, mut pl) =
|
||||||
TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded")
|
TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded")
|
||||||
.header(CONTENT_LENGTH, "11")
|
.header(CONTENT_LENGTH, "11")
|
||||||
.set_payload(Bytes::from_static(b"hello=world"))
|
.set_payload(Bytes::from_static(b"hello=world&counter=123"))
|
||||||
.to_http_parts();
|
.to_http_parts();
|
||||||
|
|
||||||
let s = block_on(Form::<Info>::from_request(&req, &mut pl)).unwrap();
|
let Form(s) = block_on(Form::<Info>::from_request(&req, &mut pl)).unwrap();
|
||||||
assert_eq!(s.hello, "world");
|
assert_eq!(
|
||||||
|
s,
|
||||||
|
Info {
|
||||||
|
hello: "world".into(),
|
||||||
|
counter: 123
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eq(err: UrlencodedError, other: UrlencodedError) -> bool {
|
fn eq(err: UrlencodedError, other: UrlencodedError) -> bool {
|
||||||
|
@ -373,14 +434,15 @@ mod tests {
|
||||||
let (req, mut pl) =
|
let (req, mut pl) =
|
||||||
TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded")
|
TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded")
|
||||||
.header(CONTENT_LENGTH, "11")
|
.header(CONTENT_LENGTH, "11")
|
||||||
.set_payload(Bytes::from_static(b"hello=world"))
|
.set_payload(Bytes::from_static(b"hello=world&counter=123"))
|
||||||
.to_http_parts();
|
.to_http_parts();
|
||||||
|
|
||||||
let info = block_on(UrlEncoded::<Info>::new(&req, &mut pl)).unwrap();
|
let info = block_on(UrlEncoded::<Info>::new(&req, &mut pl)).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
info,
|
info,
|
||||||
Info {
|
Info {
|
||||||
hello: "world".to_owned()
|
hello: "world".to_owned(),
|
||||||
|
counter: 123
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -389,15 +451,36 @@ mod tests {
|
||||||
"application/x-www-form-urlencoded; charset=utf-8",
|
"application/x-www-form-urlencoded; charset=utf-8",
|
||||||
)
|
)
|
||||||
.header(CONTENT_LENGTH, "11")
|
.header(CONTENT_LENGTH, "11")
|
||||||
.set_payload(Bytes::from_static(b"hello=world"))
|
.set_payload(Bytes::from_static(b"hello=world&counter=123"))
|
||||||
.to_http_parts();
|
.to_http_parts();
|
||||||
|
|
||||||
let info = block_on(UrlEncoded::<Info>::new(&req, &mut pl)).unwrap();
|
let info = block_on(UrlEncoded::<Info>::new(&req, &mut pl)).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
info,
|
info,
|
||||||
Info {
|
Info {
|
||||||
hello: "world".to_owned()
|
hello: "world".to_owned(),
|
||||||
|
counter: 123
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_responder() {
|
||||||
|
let req = TestRequest::default().to_http_request();
|
||||||
|
|
||||||
|
let form = Form(Info {
|
||||||
|
hello: "world".to_string(),
|
||||||
|
counter: 123,
|
||||||
|
});
|
||||||
|
let resp = form.respond_to(&req).unwrap();
|
||||||
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
assert_eq!(
|
||||||
|
resp.headers().get(CONTENT_TYPE).unwrap(),
|
||||||
|
HeaderValue::from_static("application/x-www-form-urlencoded")
|
||||||
|
);
|
||||||
|
|
||||||
|
use crate::responder::tests::BodyTest;
|
||||||
|
assert_eq!(resp.body().bin_ref(), b"hello=world&counter=123");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue