diff --git a/CHANGES.md b/CHANGES.md index f7a753543..e9970d8f8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,8 @@ ### Added +* Add `ClientRequestBuilder::form()` for sending `application/x-www-form-urlencoded` requests. + * Add methods to `HttpResponse` to retrieve, add, and delete cookies * Add `.set_content_type()` and `.set_content_disposition()` methods diff --git a/src/client/request.rs b/src/client/request.rs index bb338482b..bc8feb3e7 100644 --- a/src/client/request.rs +++ b/src/client/request.rs @@ -10,6 +10,7 @@ use futures::Stream; use percent_encoding::{percent_encode, USERINFO_ENCODE_SET}; use serde::Serialize; use serde_json; +use serde_urlencoded; use url::Url; use super::body::ClientBody; @@ -658,6 +659,24 @@ impl ClientRequestBuilder { self.body(body) } + + /// Set a urlencoded body and generate `ClientRequest` + /// + /// `ClientRequestBuilder` can not be used after this call. + pub fn form(&mut self, value: T) -> Result { + let body = serde_urlencoded::to_string(&value)?; + + let contains = if let Some(parts) = parts(&mut self.request, &self.err) { + parts.headers.contains_key(header::CONTENT_TYPE) + } else { + true + }; + if !contains { + self.header(header::CONTENT_TYPE, "application/x-www-form-urlencoded"); + } + + self.body(body) + } /// Set a streaming body and generate `ClientRequest`. /// diff --git a/src/error.rs b/src/error.rs index f3327c2b6..bbafb1c41 100644 --- a/src/error.rs +++ b/src/error.rs @@ -16,6 +16,7 @@ use http_range::HttpRangeParseError; use httparse; use serde::de::value::Error as DeError; use serde_json::error::Error as JsonError; +use serde_urlencoded::ser::Error as FormError; use tokio_timer::Error as TimerError; pub use url::ParseError as UrlParseError; @@ -205,6 +206,9 @@ impl From for Error { /// `InternalServerError` for `JsonError` impl ResponseError for JsonError {} +/// `InternalServerError` for `FormError` +impl ResponseError for FormError {} + /// `InternalServerError` for `TimerError` impl ResponseError for TimerError {} diff --git a/tests/test_handlers.rs b/tests/test_handlers.rs index 5ece53eed..8f1ee9439 100644 --- a/tests/test_handlers.rs +++ b/tests/test_handlers.rs @@ -96,6 +96,35 @@ fn test_async_extractor_async() { assert_eq!(bytes, Bytes::from_static(b"{\"test\":1}")); } +#[derive(Deserialize, Serialize)] +struct FormData { + username: String, +} + +#[test] +fn test_form_extractor() { + let mut srv = test::TestServer::new(|app| { + app.resource("/{username}/index.html", |r| { + r.route().with(|form: Form| { + format!("{}", form.username) + }) + }); + }); + + // client request + let request = srv + .post() + .uri(srv.url("/test1/index.html")) + .form(FormData{username: "test".to_string()}) + .unwrap(); + let response = srv.execute(request.send()).unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = srv.execute(response.body()).unwrap(); + assert_eq!(bytes, Bytes::from_static(b"test")); +} + #[test] fn test_path_and_query_extractor() { let mut srv = test::TestServer::new(|app| {