Switch to Result-based API. (#132)
Gets rid of synthetic_error, and makes the various send_* methods return `Result<Response, Error>`. Introduces a new error type "HTTP", which represents an error due to status codes 4xx or 5xx. The HTTP error type contains a boxed Response, so users can read the actual response if they want. Adds an `error_for_status` setting to disable the functionality of treating 4xx and 5xx as errors. Adds .unwrap() to a lot of tests. Fixes #128.
This commit is contained in:
committed by
GitHub
parent
257d4e54dd
commit
e36c1c2aa1
@@ -1,6 +1,5 @@
|
||||
use std::fmt;
|
||||
use std::io::Read;
|
||||
use std::result::Result;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time;
|
||||
|
||||
@@ -19,6 +18,8 @@ use crate::Response;
|
||||
#[cfg(feature = "json")]
|
||||
use super::SerdeValue;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// Request instances are builders that creates a request.
|
||||
///
|
||||
/// ```
|
||||
@@ -37,6 +38,7 @@ pub struct Request {
|
||||
url: String,
|
||||
|
||||
// from request itself
|
||||
return_error_for_status: bool,
|
||||
pub(crate) headers: Vec<Header>,
|
||||
pub(crate) query: QString,
|
||||
pub(crate) timeout_connect: Option<time::Duration>,
|
||||
@@ -76,6 +78,7 @@ impl Request {
|
||||
url,
|
||||
headers: agent.headers.clone(),
|
||||
redirects: 5,
|
||||
return_error_for_status: true,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
@@ -104,18 +107,22 @@ impl Request {
|
||||
///
|
||||
/// println!("{:?}", r);
|
||||
/// ```
|
||||
pub fn call(&mut self) -> Response {
|
||||
pub fn call(&mut self) -> Result<Response> {
|
||||
self.do_call(Payload::Empty)
|
||||
}
|
||||
|
||||
fn do_call(&mut self, payload: Payload) -> Response {
|
||||
self.to_url()
|
||||
.and_then(|url| {
|
||||
let reader = payload.into_read();
|
||||
let unit = Unit::new(&self, &url, true, &reader);
|
||||
unit::connect(&self, unit, true, 0, reader, false)
|
||||
})
|
||||
.unwrap_or_else(|e| e.into())
|
||||
fn do_call(&self, payload: Payload) -> Result<Response> {
|
||||
let response = self.to_url().and_then(|url| {
|
||||
let reader = payload.into_read();
|
||||
let unit = Unit::new(&self, &url, true, &reader);
|
||||
unit::connect(&self, unit, true, 0, reader, false)
|
||||
})?;
|
||||
|
||||
if response.error() && self.return_error_for_status {
|
||||
Err(Error::HTTP(response.into()))
|
||||
} else {
|
||||
Ok(response)
|
||||
}
|
||||
}
|
||||
|
||||
/// Send data a json value.
|
||||
@@ -135,7 +142,7 @@ impl Request {
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(feature = "json")]
|
||||
pub fn send_json(&mut self, data: SerdeValue) -> Response {
|
||||
pub fn send_json(&mut self, data: SerdeValue) -> Result<Response> {
|
||||
if self.header("Content-Type").is_none() {
|
||||
self.set("Content-Type", "application/json");
|
||||
}
|
||||
@@ -152,7 +159,7 @@ impl Request {
|
||||
/// .send_bytes(body);
|
||||
/// println!("{:?}", r);
|
||||
/// ```
|
||||
pub fn send_bytes(&mut self, data: &[u8]) -> Response {
|
||||
pub fn send_bytes(&mut self, data: &[u8]) -> Result<Response> {
|
||||
self.do_call(Payload::Bytes(data.to_owned()))
|
||||
}
|
||||
|
||||
@@ -177,7 +184,7 @@ impl Request {
|
||||
/// .send_string("Hällo Wörld!");
|
||||
/// println!("{:?}", r);
|
||||
/// ```
|
||||
pub fn send_string(&mut self, data: &str) -> Response {
|
||||
pub fn send_string(&mut self, data: &str) -> Result<Response> {
|
||||
let text = data.into();
|
||||
let charset =
|
||||
crate::response::charset_from_content_type(self.header("content-type")).to_string();
|
||||
@@ -199,7 +206,7 @@ impl Request {
|
||||
/// println!("{:?}", r);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn send_form(&mut self, data: &[(&str, &str)]) -> Response {
|
||||
pub fn send_form(&mut self, data: &[(&str, &str)]) -> Result<Response> {
|
||||
if self.header("Content-Type").is_none() {
|
||||
self.set("Content-Type", "application/x-www-form-urlencoded");
|
||||
}
|
||||
@@ -227,7 +234,7 @@ impl Request {
|
||||
/// .set("Content-Type", "text/plain")
|
||||
/// .send(read);
|
||||
/// ```
|
||||
pub fn send(&mut self, reader: impl Read + 'static) -> Response {
|
||||
pub fn send(&mut self, reader: impl Read + 'static) -> Result<Response> {
|
||||
self.do_call(Payload::Reader(Box::new(reader)))
|
||||
}
|
||||
|
||||
@@ -239,8 +246,8 @@ impl Request {
|
||||
/// .set("Accept", "text/plain")
|
||||
/// .call();
|
||||
///
|
||||
/// if r.ok() {
|
||||
/// println!("yay got {}", r.into_string().unwrap());
|
||||
/// if r.is_ok() {
|
||||
/// println!("yay got {}", r.unwrap().into_string().unwrap());
|
||||
/// } else {
|
||||
/// println!("Oh no error!");
|
||||
/// }
|
||||
@@ -446,8 +453,7 @@ impl Request {
|
||||
/// Defaults to `5`. Set to `0` to avoid redirects and instead
|
||||
/// get a response object with the 3xx status code.
|
||||
///
|
||||
/// If the redirect count hits this limit (and it's > 0), a synthetic 500 error
|
||||
/// response is produced.
|
||||
/// If the redirect count hits this limit (and it's > 0), TooManyRedirects is returned.
|
||||
///
|
||||
/// ```
|
||||
/// let r = ureq::get("/my_page")
|
||||
@@ -460,6 +466,25 @@ impl Request {
|
||||
self
|
||||
}
|
||||
|
||||
/// By default, if a response's status is anything but a 2xx or 3xx,
|
||||
/// send() and related methods will return an Error. If you want
|
||||
/// to handle such responses as non-errors, set this to false.
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), ureq::Error> {
|
||||
/// let result = ureq::get("http://httpbin.org/status/500")
|
||||
/// .error_for_status(false)
|
||||
/// .call();
|
||||
/// assert!(result.is_ok());
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn error_for_status(&mut self, value: bool) -> &mut Request {
|
||||
self.return_error_for_status = value;
|
||||
self
|
||||
}
|
||||
|
||||
/// Get the method this request is using.
|
||||
///
|
||||
/// Example:
|
||||
@@ -499,7 +524,7 @@ impl Request {
|
||||
/// .build();
|
||||
/// assert_eq!(req2.get_host().unwrap(), "localhost");
|
||||
/// ```
|
||||
pub fn get_host(&self) -> Result<String, Error> {
|
||||
pub fn get_host(&self) -> Result<String> {
|
||||
match self.to_url() {
|
||||
Ok(u) => match u.host_str() {
|
||||
Some(host) => Ok(host.to_string()),
|
||||
@@ -517,7 +542,7 @@ impl Request {
|
||||
/// .build();
|
||||
/// assert_eq!(req.get_scheme().unwrap(), "https");
|
||||
/// ```
|
||||
pub fn get_scheme(&self) -> Result<String, Error> {
|
||||
pub fn get_scheme(&self) -> Result<String> {
|
||||
self.to_url().map(|u| u.scheme().to_string())
|
||||
}
|
||||
|
||||
@@ -530,7 +555,7 @@ impl Request {
|
||||
/// .build();
|
||||
/// assert_eq!(req.get_query().unwrap(), "?foo=bar&format=json");
|
||||
/// ```
|
||||
pub fn get_query(&self) -> Result<String, Error> {
|
||||
pub fn get_query(&self) -> Result<String> {
|
||||
self.to_url()
|
||||
.map(|u| unit::combine_query(&u, &self.query, true))
|
||||
}
|
||||
@@ -543,11 +568,11 @@ impl Request {
|
||||
/// .build();
|
||||
/// assert_eq!(req.get_path().unwrap(), "/innit");
|
||||
/// ```
|
||||
pub fn get_path(&self) -> Result<String, Error> {
|
||||
pub fn get_path(&self) -> Result<String> {
|
||||
self.to_url().map(|u| u.path().to_string())
|
||||
}
|
||||
|
||||
fn to_url(&self) -> Result<Url, Error> {
|
||||
fn to_url(&self) -> Result<Url> {
|
||||
Url::parse(&self.url).map_err(|e| Error::BadUrl(format!("{}", e)))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user