diff --git a/Cargo.toml b/Cargo.toml
index ecf8c12..74b1f78 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,6 +13,7 @@ hyper = { version = "0.9" , default-features = false }
log = "0.3"
serde = "0.8"
serde_json = "0.8"
+serde_urlencoded = "0.3"
url = "1.0"
[dependencies.native-tls]
diff --git a/src/client.rs b/src/client.rs
index 6952367..ef61450 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -9,6 +9,7 @@ use hyper::{Url};
use serde::Serialize;
use serde_json;
+use serde_urlencoded;
use ::body::{self, Body};
@@ -94,7 +95,7 @@ pub struct RequestBuilder<'a> {
_version: HttpVersion,
headers: Headers,
- body: Option
,
+ body: Option<::Result>,
}
impl<'a> RequestBuilder<'a> {
@@ -122,7 +123,30 @@ impl<'a> RequestBuilder<'a> {
/// Set the request body.
pub fn body>(mut self, body: T) -> RequestBuilder<'a> {
- self.body = Some(body.into());
+ self.body = Some(Ok(body.into()));
+ self
+ }
+
+ /// Send a form body.
+ ///
+ /// Sets the body to the url encoded serialization of the passed value,
+ /// and also sets the `Content-Type: application/www-form-url-encoded`
+ /// header.
+ ///
+ /// ```no_run
+ /// # use std::collections::HashMap;
+ /// let mut params = HashMap::new();
+ /// params.insert("lang", "rust");
+ ///
+ /// let client = reqwest::Client::new().unwrap();
+ /// let res = client.post("http://httpbin.org")
+ /// .form(¶ms)
+ /// .send();
+ /// ```
+ pub fn form(mut self, form: &T) -> RequestBuilder<'a> {
+ let body = serde_urlencoded::to_string(form).map_err(::Error::from);
+ self.headers.set(ContentType::form_url_encoded());
+ self.body = Some(body.map(|b| b.into()));
self
}
@@ -136,14 +160,15 @@ impl<'a> RequestBuilder<'a> {
/// let mut map = HashMap::new();
/// map.insert("lang", "rust");
///
- /// let res = reqwest::post("http://www.rust-lang.org")
- /// .json(map)
+ /// let client = reqwest::Client::new().unwrap();
+ /// let res = client.post("http://httpbin.org")
+ /// .json(&map)
/// .send();
/// ```
- pub fn json(mut self, json: T) -> RequestBuilder<'a> {
- let body = serde_json::to_vec(&json).expect("serde to_vec cannot fail");
+ pub fn json(mut self, json: &T) -> RequestBuilder<'a> {
+ let body = serde_json::to_vec(json).expect("serde to_vec cannot fail");
self.headers.set(ContentType::json());
- self.body = Some(body.into());
+ self.body = Some(Ok(body.into()));
self
}
@@ -157,7 +182,10 @@ impl<'a> RequestBuilder<'a> {
let mut method = self.method;
let mut url = try!(self.url);
let mut headers = self.headers;
- let mut body = self.body;
+ let mut body = match self.body {
+ Some(b) => Some(try!(b)),
+ None => None,
+ };
let mut redirect_count = 0;
diff --git a/src/error.rs b/src/error.rs
index 00167b3..e78c13c 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -6,6 +6,11 @@ use std::fmt;
pub enum Error {
/// An HTTP error from the `hyper` crate.
Http(::hyper::Error),
+ /// An error trying to serialize a value.
+ ///
+ /// This may be serializing a value that is illegal in JSON or
+ /// form-url-encoded bodies.
+ Serialize(Box),
/// A request tried to redirect too many times.
TooManyRedirects,
#[doc(hidden)]
@@ -16,6 +21,7 @@ impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::Http(ref e) => fmt::Display::fmt(e, f),
+ Error::Serialize(ref e) => fmt::Display::fmt(e, f),
Error::TooManyRedirects => {
f.pad("Too many redirects")
},
@@ -28,6 +34,7 @@ impl StdError for Error {
fn description(&self) -> &str {
match *self {
Error::Http(ref e) => e.description(),
+ Error::Serialize(ref e) => e.description(),
Error::TooManyRedirects => "Too many redirects",
Error::__DontMatchMe => unreachable!()
}
@@ -36,6 +43,7 @@ impl StdError for Error {
fn cause(&self) -> Option<&StdError> {
match *self {
Error::Http(ref e) => Some(e),
+ Error::Serialize(ref e) => Some(&**e),
Error::TooManyRedirects => None,
Error::__DontMatchMe => unreachable!()
}
@@ -54,7 +62,11 @@ impl From<::url::ParseError> for Error {
}
}
-
+impl From<::serde_urlencoded::ser::Error> for Error {
+ fn from(err: ::serde_urlencoded::ser::Error) -> Error {
+ Error::Serialize(Box::new(err))
+ }
+}
/// A `Result` alias where the `Err` case is `reqwest::Error`.
pub type Result = ::std::result::Result;
diff --git a/src/lib.rs b/src/lib.rs
index 7799a37..ee2bf12 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -9,9 +9,9 @@
//! to do for them.
//!
//! - Uses system-native TLS
-//! - Plain bodies, JSON, urlencoded, multipart
-//! - Customizable redirect policy
-//! - Cookies
+//! - Plain bodies, JSON, urlencoded, (TODO: multipart)
+//! - (TODO: Customizable redirect policy)
+//! - (TODO: Cookies)
//!
//! The `reqwest::Client` is synchronous, making it a great fit for
//! applications that only require a few HTTP requests, and wish to handle
@@ -32,12 +32,69 @@
//!
//! If you plan to perform multiple requests, it is best to create a [`Client`][client]
//! and reuse it, taking advantage of keep-alive connection pooling.
+//!
+//! ## Making POST requests (or setting request bodies)
+//!
+//! There are several ways you can set the body of a request. The basic one is
+//! by using the `body()` method of a [`RequestBuilder`][builder]. This lets you set the
+//! exact raw bytes of what the body should be. It accepts various types,
+//! including `String`, `Vec`, and `File`. If you wish to pass a custom
+//! Reader, you can use the `reqwest::Body::new()` constructor.
+//!
+//! ```no_run
+//! let client = reqwest::Client::new().unwrap();
+//! let res = client.post("http://httpbin.org/post")
+//! .body("the exact body that is sent")
+//! .send();
+//! ```
+//!
+//! ### Forms
+//!
+//! It's very common to want to send form data in a request body. This can be
+//! done with any type that can be serialized into form data.
+//!
+//! This can be an array of tuples, or a `HashMap`, or a custom type that
+//! implements [`Serialize`][serde].
+//!
+//! ```no_run
+//! // This will POST a body of `foo=bar&baz=quux`
+//! let params = [("foo", "bar"), ("baz", "quux")];
+//! let client = reqwest::Client::new().unwrap();
+//! let res = client.post("http://httpbin.org/post")
+//! .form(¶ms)
+//! .send();
+//! ```
+//!
+//! ### JSON
+//!
+//! There is also a `json` method helper on the [`RequestBuilder`][builder] that works in
+//! a similar fashion the `form` method. It can take any value that can be
+//! serialized into JSON.
+//!
+//! ```no_run
+//! # use std::collections::HashMap;
+//! // This will POST a body of `{"lang":"rust","body":"json"}`
+//! let mut map = HashMap::new();
+//! map.insert("lang", "rust");
+//! map.insert("body", "json");
+//!
+//! let client = reqwest::Client::new().unwrap();
+//! let res = client.post("http://httpbin.org/post")
+//! .json(&map)
+//! .send();
+//! ```
+//!
+//! [hyper]: http://hyper.rs
+//! [client]: ./struct.Client.html
+//! [builder]: ./client/struct.RequestBuilder.html
+//! [serde]: http://serde.rs
extern crate hyper;
#[macro_use] extern crate log;
#[cfg(feature = "tls")] extern crate native_tls;
extern crate serde;
extern crate serde_json;
+extern crate serde_urlencoded;
extern crate url;
pub use hyper::header;