mirror of https://github.com/rwf2/Rocket.git
Document ResponseBuilder.
This commit is contained in:
parent
5540201236
commit
abdb8c2aa1
|
@ -56,7 +56,7 @@ simple.
|
|||
|
||||
* Core: `cd lib && cargo build`
|
||||
* Codegen: `cd codegen && cargo build`
|
||||
* Contrib: `cd contrib && cargo build`
|
||||
* Contrib: `cd contrib && cargo build --all-features`
|
||||
|
||||
### Examples
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ mod failure;
|
|||
pub mod content;
|
||||
pub mod status;
|
||||
|
||||
pub use self::response::{Response, Body, DEFAULT_CHUNK_SIZE};
|
||||
pub use self::response::{Response, ResponseBuilder, Body, DEFAULT_CHUNK_SIZE};
|
||||
pub use self::responder::Responder;
|
||||
pub use self::redirect::Redirect;
|
||||
pub use self::flash::Flash;
|
||||
|
|
|
@ -166,7 +166,7 @@ pub trait Responder<'r> {
|
|||
/// let body_string = response.body().and_then(|b| b.into_string());
|
||||
/// assert_eq!(body_string, Some("Hello".to_string()));
|
||||
///
|
||||
/// let content_type: Vec<_> = response.get_header_values("Content-Type").collect();
|
||||
/// let content_type: Vec<_> = response.header_values("Content-Type").collect();
|
||||
/// assert_eq!(content_type.len(), 1);
|
||||
/// assert_eq!(content_type[0], ContentType::Plain.to_string());
|
||||
/// ```
|
||||
|
|
|
@ -18,7 +18,7 @@ pub enum Body<T> {
|
|||
}
|
||||
|
||||
impl<T> Body<T> {
|
||||
/// Returns a mutable borrow to the inner type.
|
||||
/// Returns a new `Body` with a mutable borrow to `self`'s inner type.
|
||||
pub fn as_mut(&mut self) -> Body<&mut T> {
|
||||
match *self {
|
||||
Body::Sized(ref mut b, n) => Body::Sized(b, n),
|
||||
|
@ -64,11 +64,80 @@ impl<T> fmt::Debug for Body<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Type for easily building `Response`s.
|
||||
///
|
||||
/// Building a [Response](struct.Response.html) can be a low-level ordeal; this
|
||||
/// structure presents a higher-level API that simplified building `Response`s.
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// `ResponseBuilder` follows the builder pattern and is usually obtained by
|
||||
/// calling [build](struct.Response.html#method.build) on `Response`. Almost all
|
||||
/// methods take the current builder as a mutable reference and return the same
|
||||
/// mutable reference with field(s) modified in the `Responder` being built.
|
||||
/// These method calls can be chained: `build.a().b()`.
|
||||
///
|
||||
/// To finish building and retrieve the built `Response`, use the
|
||||
/// [finalize](#method.finalize) or [ok](#method.ok) methods.
|
||||
///
|
||||
/// ## Headers
|
||||
///
|
||||
/// When building a `Response`, headers can either be _replaced_ or _adjoined_;
|
||||
/// the default behavior (using `header(..)`) is to _replace_. When a header is
|
||||
/// _replaced_, any existing values for headers with the same name are removed,
|
||||
/// and the new value is set. If no header exists, the header is simply added.
|
||||
/// On the other hand, when a header is `adjoined`, all existing values will
|
||||
/// remain, and the `value` of the adjoined header will be added to the set of
|
||||
/// existing values, if any. Adjoining maintains order: headers adjoined first
|
||||
/// will appear first in the `Response`.
|
||||
///
|
||||
/// ## Joining and Merging
|
||||
///
|
||||
/// It is often necessary to combine multiple `Response`s in some way. The
|
||||
/// [merge](#method.merge) and [join](#method.join) methods facilitate this. The
|
||||
/// `merge` method replaces all of the fields in `self` with those present in
|
||||
/// `other`. The `join` method sets any fields not set in `self` to the value in
|
||||
/// `other`. See their documentation for more details.
|
||||
/// ## Example
|
||||
///
|
||||
/// The following example builds a `Response` with:
|
||||
///
|
||||
/// * **Status**: `418 I'm a teapot`
|
||||
/// * **Content-Type** header: `text/plain; charset=utf-8`
|
||||
/// * **X-Teapot-Make** header: `Rocket`
|
||||
/// * **X-Teapot-Model** headers: `Utopia`, `Series 1`
|
||||
/// * **Body**: fixed-size string `"Brewing the best coffee!"`
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::io::Cursor;
|
||||
/// use rocket::response::Response;
|
||||
/// use rocket::http::{Status, ContentType};
|
||||
///
|
||||
/// let response = Response::build()
|
||||
/// .status(Status::ImATeapot)
|
||||
/// .header(ContentType::Plain)
|
||||
/// .raw_header("X-Teapot-Make", "Rocket")
|
||||
/// .raw_header("X-Teapot-Model", "Utopia")
|
||||
/// .raw_header_adjoin("X-Teapot-Model", "Series 1")
|
||||
/// .sized_body(Cursor::new("Brewing the best coffee!"))
|
||||
/// .finalize();
|
||||
/// ```
|
||||
///
|
||||
pub struct ResponseBuilder<'r> {
|
||||
response: Response<'r>
|
||||
}
|
||||
|
||||
impl<'r> ResponseBuilder<'r> {
|
||||
/// Creates a new `ResponseBuilder` that will build on top of the `base`
|
||||
/// `Response`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::response::{ResponseBuilder, Response};
|
||||
///
|
||||
/// let builder = ResponseBuilder::new(Response::new());
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn new(base: Response<'r>) -> ResponseBuilder<'r> {
|
||||
ResponseBuilder {
|
||||
|
@ -76,12 +145,36 @@ impl<'r> ResponseBuilder<'r> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Sets the status of the `Response` being built to `status`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::Response;
|
||||
/// use rocket::http::Status;
|
||||
///
|
||||
/// let response = Response::build()
|
||||
/// .status(Status::NotFound)
|
||||
/// .finalize();
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn status(&mut self, status: Status) -> &mut ResponseBuilder<'r> {
|
||||
self.response.set_status(status);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the status of the `Response` being built to a custom status
|
||||
/// constructed from the `code` and `reason` phrase.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::Response;
|
||||
///
|
||||
/// let response = Response::build()
|
||||
/// .raw_status(699, "Alien Encounter")
|
||||
/// .finalize();
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn raw_status(&mut self, code: u16, reason: &'static str)
|
||||
-> &mut ResponseBuilder<'r> {
|
||||
|
@ -89,6 +182,24 @@ impl<'r> ResponseBuilder<'r> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Adds `header` to the `Response`, replacing any header with the same name
|
||||
/// that already exists in the response. If multiple headers with
|
||||
/// the same name exist, they are all removed, and only the new header and
|
||||
/// value will remain.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::Response;
|
||||
/// use rocket::http::ContentType;
|
||||
///
|
||||
/// let response = Response::build()
|
||||
/// .header(ContentType::JSON)
|
||||
/// .header(ContentType::HTML)
|
||||
/// .finalize();
|
||||
///
|
||||
/// assert_eq!(response.header_values("Content-Type").count(), 1);
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn header<'h: 'r, H>(&mut self, header: H) -> &mut ResponseBuilder<'r>
|
||||
where H: Into<Header<'h>>
|
||||
|
@ -97,6 +208,24 @@ impl<'r> ResponseBuilder<'r> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Adds `header` to the `Response` by adjoining the header with any
|
||||
/// existing headers with the same name that already exist in the
|
||||
/// `Response`. This allow for multiple headers with the same name and
|
||||
/// potentially different values to be present in the `Response`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::Response;
|
||||
/// use rocket::http::hyper::header::Accept;
|
||||
///
|
||||
/// let response = Response::build()
|
||||
/// .header_adjoin(Accept::json())
|
||||
/// .header_adjoin(Accept::text())
|
||||
/// .finalize();
|
||||
///
|
||||
/// assert_eq!(response.header_values("Accept").count(), 2);
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn header_adjoin<'h: 'r, H>(&mut self, header: H) -> &mut ResponseBuilder<'r>
|
||||
where H: Into<Header<'h>>
|
||||
|
@ -105,6 +234,24 @@ impl<'r> ResponseBuilder<'r> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Adds custom a header to the `Response` with the given name and value,
|
||||
/// replacing any header with the same name that already exists in the
|
||||
/// response. If multiple headers with the same name exist, they are all
|
||||
/// removed, and only the new header and value will remain.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::Response;
|
||||
/// use rocket::http::ContentType;
|
||||
///
|
||||
/// let response = Response::build()
|
||||
/// .raw_header("X-Custom", "first")
|
||||
/// .raw_header("X-Custom", "second")
|
||||
/// .finalize();
|
||||
///
|
||||
/// assert_eq!(response.header_values("X-Custom").count(), 1);
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn raw_header<'a: 'r, 'b: 'r, N, V>(&mut self, name: N, value: V)
|
||||
-> &mut ResponseBuilder<'r>
|
||||
|
@ -114,6 +261,24 @@ impl<'r> ResponseBuilder<'r> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Adds custom header to the `Response` with the given name and value,
|
||||
/// adjoining the header with any existing headers with the same name that
|
||||
/// already exist in the `Response`. This allow for multiple headers with
|
||||
/// the same name and potentially different values to be present in the
|
||||
/// `Response`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::Response;
|
||||
///
|
||||
/// let response = Response::build()
|
||||
/// .raw_header_adjoin("X-Custom", "first")
|
||||
/// .raw_header_adjoin("X-Custom", "second")
|
||||
/// .finalize();
|
||||
///
|
||||
/// assert_eq!(response.header_values("X-Custom").count(), 2);
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn raw_header_adjoin<'a: 'r, 'b: 'r, N, V>(&mut self, name: N, value: V)
|
||||
-> &mut ResponseBuilder<'r>
|
||||
|
@ -123,6 +288,22 @@ impl<'r> ResponseBuilder<'r> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Sets the body of the `Response` to be the fixed-sized `body`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::Response;
|
||||
/// use std::fs::File;
|
||||
/// # use std::io;
|
||||
///
|
||||
/// # fn test() -> io::Result<()> {
|
||||
/// let response = Response::build()
|
||||
/// .sized_body(File::open("body.txt")?)
|
||||
/// .finalize();
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn sized_body<B>(&mut self, body: B) -> &mut ResponseBuilder<'r>
|
||||
where B: io::Read + io::Seek + 'r
|
||||
|
@ -131,6 +312,22 @@ impl<'r> ResponseBuilder<'r> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Sets the body of the `Response` to be the streamed `body`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::Response;
|
||||
/// use std::fs::File;
|
||||
/// # use std::io;
|
||||
///
|
||||
/// # fn test() -> io::Result<()> {
|
||||
/// let response = Response::build()
|
||||
/// .streamed_body(File::open("body.txt")?)
|
||||
/// .finalize();
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn streamed_body<B>(&mut self, body: B) -> &mut ResponseBuilder<'r>
|
||||
where B: io::Read + 'r
|
||||
|
@ -139,6 +336,23 @@ impl<'r> ResponseBuilder<'r> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Sets the body of the `Response` to be the streamed `body` with a custom
|
||||
/// chunk size, in bytes.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::Response;
|
||||
/// use std::fs::File;
|
||||
/// # use std::io;
|
||||
///
|
||||
/// # fn test() -> io::Result<()> {
|
||||
/// let response = Response::build()
|
||||
/// .chunked_body(File::open("body.txt")?, 8096)
|
||||
/// .finalize();
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn chunked_body<B: io::Read + 'r>(&mut self, body: B, chunk_size: u64)
|
||||
-> &mut ResponseBuilder<'r>
|
||||
|
@ -147,23 +361,122 @@ impl<'r> ResponseBuilder<'r> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Merges the `other` `Response` into `self` by setting any fields in
|
||||
/// `self` to the corresponding value in `other` if they are set in `other`.
|
||||
/// Fields in `self` are unchanged if they are not set in `other`. If a
|
||||
/// header is set in both `self` and `other`, the values in `other` are
|
||||
/// kept. Headers set only in `self` remain.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::Response;
|
||||
/// use rocket::http::{Status, ContentType};
|
||||
///
|
||||
/// let base = Response::build()
|
||||
/// .status(Status::NotFound)
|
||||
/// .header(ContentType::HTML)
|
||||
/// .raw_header("X-Custom", "value 1")
|
||||
/// .finalize();
|
||||
///
|
||||
/// let response = Response::build()
|
||||
/// .status(Status::ImATeapot)
|
||||
/// .raw_header("X-Custom", "value 2")
|
||||
/// .raw_header_adjoin("X-Custom", "value 3")
|
||||
/// .merge(base)
|
||||
/// .finalize();
|
||||
///
|
||||
/// assert_eq!(response.status(), Status::NotFound);
|
||||
///
|
||||
/// # {
|
||||
/// let ctype: Vec<_> = response.header_values("Content-Type").collect();
|
||||
/// assert_eq!(ctype, vec![ContentType::HTML.to_string()]);
|
||||
/// # }
|
||||
///
|
||||
/// # {
|
||||
/// let custom_values: Vec<_> = response.header_values("X-Custom").collect();
|
||||
/// assert_eq!(custom_values, vec!["value 1"]);
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn merge(&mut self, other: Response<'r>) -> &mut ResponseBuilder<'r> {
|
||||
self.response.merge(other);
|
||||
self
|
||||
}
|
||||
|
||||
/// Joins the `other` `Response` into `self` by setting any fields in `self`
|
||||
/// to the corresponding value in `other` if they are set in `self`. Fields
|
||||
/// in `self` are unchanged if they are already set. If a header is set in
|
||||
/// both `self` and `other`, the values are adjoined, with the values in
|
||||
/// `self` coming first. Headers only in `self` or `other` are set in
|
||||
/// `self`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::Response;
|
||||
/// use rocket::http::{Status, ContentType};
|
||||
///
|
||||
/// let other = Response::build()
|
||||
/// .status(Status::NotFound)
|
||||
/// .header(ContentType::HTML)
|
||||
/// .raw_header("X-Custom", "value 1")
|
||||
/// .finalize();
|
||||
///
|
||||
/// let response = Response::build()
|
||||
/// .status(Status::ImATeapot)
|
||||
/// .raw_header("X-Custom", "value 2")
|
||||
/// .raw_header_adjoin("X-Custom", "value 3")
|
||||
/// .join(other)
|
||||
/// .finalize();
|
||||
///
|
||||
/// assert_eq!(response.status(), Status::ImATeapot);
|
||||
///
|
||||
/// # {
|
||||
/// let ctype: Vec<_> = response.header_values("Content-Type").collect();
|
||||
/// assert_eq!(ctype, vec![ContentType::HTML.to_string()]);
|
||||
/// # }
|
||||
///
|
||||
/// # {
|
||||
/// let custom_values: Vec<_> = response.header_values("X-Custom").collect();
|
||||
/// assert_eq!(custom_values, vec!["value 2", "value 3", "value 1"]);
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn join(&mut self, other: Response<'r>) -> &mut ResponseBuilder<'r> {
|
||||
self.response.join(other);
|
||||
self
|
||||
}
|
||||
|
||||
/// Retrieve the built `Response`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::Response;
|
||||
///
|
||||
/// let response = Response::build()
|
||||
/// // build the response
|
||||
/// .finalize();
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn finalize(&mut self) -> Response<'r> {
|
||||
::std::mem::replace(&mut self.response, Response::new())
|
||||
}
|
||||
|
||||
/// Retrieve the built `Response` wrapped in `Ok`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::Response;
|
||||
///
|
||||
/// let response: Result<Response, ()> = Response::build()
|
||||
/// // build the response
|
||||
/// .ok();
|
||||
///
|
||||
/// assert!(response.is_ok());
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn ok<T>(&mut self) -> Result<Response<'r>, T> {
|
||||
Ok(self.finalize())
|
||||
|
@ -173,7 +486,7 @@ impl<'r> ResponseBuilder<'r> {
|
|||
// `join`? Maybe one does one thing, the other does another? IE: `merge`
|
||||
// replaces, `join` adds. One more thing that could be done: we could make it
|
||||
// some that _some_ headers default to replacing, and other to joining.
|
||||
/// An HTTP response.
|
||||
/// An HTTP/Rocket response, returned by `Responder`s.
|
||||
#[derive(Default)]
|
||||
pub struct Response<'r> {
|
||||
status: Option<Status>,
|
||||
|
@ -222,8 +535,7 @@ impl<'r> Response<'r> {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get_header_values<'h>(&'h self, name: &str)
|
||||
-> impl Iterator<Item=&'h str> {
|
||||
pub fn header_values<'h>(&'h self, name: &str) -> impl Iterator<Item=&'h str> {
|
||||
self.headers.get(name)
|
||||
}
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ fn auto_head() {
|
|||
}
|
||||
|
||||
|
||||
let content_type: Vec<_> = response.get_header_values("Content-Type").collect();
|
||||
let content_type: Vec<_> = response.header_values("Content-Type").collect();
|
||||
assert_eq!(content_type, vec![ContentType::Plain.to_string()]);
|
||||
|
||||
let mut req = MockRequest::new(Head, "/empty");
|
||||
|
@ -67,6 +67,6 @@ fn user_head() {
|
|||
|
||||
assert_eq!(response.status(), Status::Ok);
|
||||
|
||||
let content_type: Vec<_> = response.get_header_values("Content-Type").collect();
|
||||
let content_type: Vec<_> = response.header_values("Content-Type").collect();
|
||||
assert_eq!(content_type, vec![ContentType::JSON.to_string()]);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue