mirror of https://github.com/rwf2/Rocket.git
Document all of the core response types.
This commit is contained in:
parent
129268506e
commit
553082f026
|
@ -77,7 +77,9 @@ impl ContentType {
|
|||
|
||||
/// Returns the Content-Type associated with the extension `ext`. Not all
|
||||
/// extensions are recognized. If an extensions is not recognized, then this
|
||||
/// method returns a ContentType of `any`.
|
||||
/// method returns a ContentType of `any`. The currently recognized
|
||||
/// extensions are: txt, html, htm, xml, js, css, json, png, gif, bmp, jpeg,
|
||||
/// jpg, and pdf.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
|
|
|
@ -7,34 +7,79 @@ use response::{Responder, Outcome};
|
|||
use http::hyper::{header, FreshHyperResponse};
|
||||
use http::ContentType;
|
||||
|
||||
/// A file with an associated name; responds with the Content-Type based on the
|
||||
/// file extension.
|
||||
#[derive(Debug)]
|
||||
pub struct NamedFile(PathBuf, File);
|
||||
|
||||
impl NamedFile {
|
||||
/// Attempts to open a file in read-only mode.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if path does not already exist. Other
|
||||
/// errors may also be returned according to
|
||||
/// [OpenOptions::open](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.open).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::response::NamedFile;
|
||||
///
|
||||
/// let file = NamedFile::open("foo.txt");
|
||||
/// ```
|
||||
pub fn open<P: AsRef<Path>>(path: P) -> io::Result<NamedFile> {
|
||||
let file = File::open(path.as_ref())?;
|
||||
Ok(NamedFile(path.as_ref().to_path_buf(), file))
|
||||
}
|
||||
|
||||
/// Retrieve the underlying `File`.
|
||||
#[inline(always)]
|
||||
pub fn file(&self) -> &File {
|
||||
&self.1
|
||||
}
|
||||
|
||||
/// Retrieve a mutable borrow to the underlying `File`.
|
||||
#[inline(always)]
|
||||
pub fn file_mut(&mut self) -> &mut File {
|
||||
&mut self.1
|
||||
}
|
||||
|
||||
/// Retrieve the path of this file.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use std::io;
|
||||
/// use rocket::response::NamedFile;
|
||||
///
|
||||
/// # fn demo_path() -> io::Result<()> {
|
||||
/// let file = NamedFile::open("foo.txt")?;
|
||||
/// assert_eq!(file.path().as_os_str(), "foo.txt");
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn path(&self) -> &Path {
|
||||
self.0.as_path()
|
||||
}
|
||||
}
|
||||
|
||||
/// Streams the named file to the client. Sets or overrides the Content-Type in
|
||||
/// the response according to the file's extension if the extension is
|
||||
/// recognized. See
|
||||
/// [ContentType::from_extension](/rocket/http/struct.ContentType.html#method.from_extension)
|
||||
/// for more information. If you would like to stream a file with a different
|
||||
/// Content-Type than that implied by its extension, use a `File` directly.
|
||||
///
|
||||
/// # Failure
|
||||
///
|
||||
/// If reading the file fails permanently at any point during the response, an
|
||||
/// `Outcome` of `Failure` is returned, and the response is terminated abrubtly.
|
||||
impl Responder for NamedFile {
|
||||
fn respond<'a>(&mut self, mut res: FreshHyperResponse<'a>) -> Outcome<'a> {
|
||||
if let Some(ext) = self.path().extension() {
|
||||
// TODO: Use Cow for lowercase.
|
||||
let ext_string = ext.to_string_lossy().to_lowercase();
|
||||
let content_type = ContentType::from_extension(&ext_string);
|
||||
if !content_type.is_any() {
|
||||
|
|
|
@ -2,31 +2,102 @@ use response::{Outcome, Responder};
|
|||
use http::hyper::{header, FreshHyperResponse, StatusCode};
|
||||
use outcome::IntoOutcome;
|
||||
|
||||
/// An empty redirect response to a given URL.
|
||||
///
|
||||
/// This type simplifies returning a redirect response to the client.
|
||||
#[derive(Debug)]
|
||||
pub struct Redirect(StatusCode, String);
|
||||
|
||||
impl Redirect {
|
||||
/// Construct a temporary "see other" (303) redirect response. This is the
|
||||
/// typical response when redirecting a user to another page. This type of
|
||||
/// redirect indicates that the client should look elsewhere, but always via
|
||||
/// a `GET` request, for a given resource.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::response::Redirect;
|
||||
///
|
||||
/// let redirect = Redirect::to("/other_url");
|
||||
/// ```
|
||||
pub fn to(uri: &str) -> Redirect {
|
||||
Redirect(StatusCode::Found, String::from(uri))
|
||||
}
|
||||
|
||||
pub fn created(uri: &str) -> Redirect {
|
||||
Redirect(StatusCode::Created, String::from(uri))
|
||||
}
|
||||
|
||||
pub fn other(uri: &str) -> Redirect {
|
||||
Redirect(StatusCode::SeeOther, String::from(uri))
|
||||
}
|
||||
|
||||
/// Construct a "temporary" (307) redirect response. This response instructs
|
||||
/// the client to reissue the current request to a different URL,
|
||||
/// maintaining the contents of the request identically. This means that,
|
||||
/// for example, a `POST` request will be resent, contents included, to the
|
||||
/// requested URL.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::response::Redirect;
|
||||
///
|
||||
/// let redirect = Redirect::temporary("/other_url");
|
||||
/// ```
|
||||
pub fn temporary(uri: &str) -> Redirect {
|
||||
Redirect(StatusCode::TemporaryRedirect, String::from(uri))
|
||||
}
|
||||
|
||||
/// Construct a "permanent" (308) redirect response. This redirect must only
|
||||
/// be used for permanent redirects as it is cached by clients. This
|
||||
/// response instructs the client to reissue requests tot he current URL to
|
||||
/// a different URL, now and in the future, maintaining the contents of the
|
||||
/// request identically. This means that, for example, a `POST` request will
|
||||
/// be resent, contents included, to the requested URL.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::response::Redirect;
|
||||
///
|
||||
/// let redirect = Redirect::permanent("/other_url");
|
||||
/// ```
|
||||
pub fn permanent(uri: &str) -> Redirect {
|
||||
Redirect(StatusCode::PermanentRedirect, String::from(uri))
|
||||
}
|
||||
|
||||
pub fn temporary(uri: &str) -> Redirect {
|
||||
Redirect(StatusCode::TemporaryRedirect, String::from(uri))
|
||||
/// Construct a temporary "found" (302) redirect response. This response
|
||||
/// instructs the client to reissue the current request to a different URL,
|
||||
/// ideally maintaining the contents of the request identically.
|
||||
/// Unfortunately, different clients may respond differently to this type of
|
||||
/// redirect, so `303` or `307` redirects, which disambiguate, are
|
||||
/// preferred.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::response::Redirect;
|
||||
///
|
||||
/// let redirect = Redirect::found("/other_url");
|
||||
/// ```
|
||||
pub fn found(uri: &str) -> Redirect {
|
||||
Redirect(StatusCode::Found, String::from(uri))
|
||||
}
|
||||
|
||||
/// Construct a permanent "moved" (301) redirect response. This response
|
||||
/// should only be used for permanent redirects as it can be cached by
|
||||
/// browsers. Because different clients may respond differently to this type
|
||||
/// of redirect, a `308` redirect, which disambiguates, is preferred.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::response::Redirect;
|
||||
///
|
||||
/// let redirect = Redirect::moved("/other_url");
|
||||
/// ```
|
||||
pub fn moved(uri: &str) -> Redirect {
|
||||
Redirect(StatusCode::MovedPermanently, String::from(uri))
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a response with the appropriate status code and the given URL in
|
||||
/// the `Location` header field. The body of the response is empty. This
|
||||
/// responder does not fail.
|
||||
impl<'a> Responder for Redirect {
|
||||
fn respond<'b>(&mut self, mut res: FreshHyperResponse<'b>) -> Outcome<'b> {
|
||||
res.headers_mut().set(header::ContentLength(0));
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use std::io::{Read, Write};
|
||||
use std::fs::File;
|
||||
use std::fmt;
|
||||
|
||||
|
@ -6,6 +5,8 @@ use http::mime::{Mime, TopLevel, SubLevel};
|
|||
use http::hyper::{header, FreshHyperResponse, StatusCode};
|
||||
use outcome::{self, IntoOutcome};
|
||||
use outcome::Outcome::*;
|
||||
use response::Stream;
|
||||
|
||||
|
||||
/// Type alias for the `Outcome` of a `Responder`.
|
||||
pub type Outcome<'a> = outcome::Outcome<(), (), (StatusCode, FreshHyperResponse<'a>)>;
|
||||
|
@ -55,6 +56,47 @@ impl<'a, T, E> IntoOutcome<(), (), (StatusCode, FreshHyperResponse<'a>)> for Res
|
|||
/// `StatusCode`. This requires that a response wasn't started and thus is
|
||||
/// still fresh.
|
||||
///
|
||||
/// # Provided Implementations
|
||||
///
|
||||
/// Rocket implements `Responder` for several standard library types. Their
|
||||
/// behavior is documented here. Note that the `Result` implementation is
|
||||
/// overloaded, allowing for two `Responder`s to be used at once, depending on
|
||||
/// the variant.
|
||||
///
|
||||
/// * **impl<'a> Responder for &'a str**
|
||||
///
|
||||
/// Sets the `Content-Type`t to `text/plain` if it is not already set. Sends
|
||||
/// the string as the body of the response.
|
||||
///
|
||||
/// * **impl Responder for String**
|
||||
///
|
||||
/// Sets the `Content-Type`t to `text/html` if it is not already set. Sends
|
||||
/// the string as the body of the response.
|
||||
///
|
||||
/// * **impl Responder for File**
|
||||
///
|
||||
/// Streams the `File` to the client. This is essentially an alias to
|
||||
/// Stream<File>.
|
||||
///
|
||||
/// * **impl<T: Responder> Responder for Option<T>**
|
||||
///
|
||||
/// If the `Option` is `Some`, the wrapped responder is used to respond to
|
||||
/// respond to the client. Otherwise, the response is forwarded to the 404
|
||||
/// error catcher and a warning is printed to the console.
|
||||
///
|
||||
/// * **impl<T: Responder, E: Debug> Responder for Result<T, E>**
|
||||
///
|
||||
/// If the `Result` is `Ok`, the wrapped responder is used to respond to the
|
||||
/// client. Otherwise, the response is forwarded to the 500 error catcher
|
||||
/// and the error is printed to the console using the `Debug`
|
||||
/// implementation.
|
||||
///
|
||||
/// * **impl<T: Responder, E: Responder + Debug> Responder for Result<T, E>**
|
||||
///
|
||||
/// If the `Result` is `Ok`, the wrapped `Ok` responder is used to respond
|
||||
/// to the client. If the `Result` is `Err`, the wrapped error responder is
|
||||
/// used to respond to the client.
|
||||
///
|
||||
/// # Implementation Tips
|
||||
///
|
||||
/// This section describes a few best practices to take into account when
|
||||
|
@ -85,6 +127,8 @@ pub trait Responder {
|
|||
fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> Outcome<'a>;
|
||||
}
|
||||
|
||||
/// Sets the `Content-Type`t to `text/plain` if it is not already set. Sends the
|
||||
/// string as the body of the response.
|
||||
impl<'a> Responder for &'a str {
|
||||
fn respond<'b>(&mut self, mut res: FreshHyperResponse<'b>) -> Outcome<'b> {
|
||||
if res.headers().get::<header::ContentType>().is_none() {
|
||||
|
@ -107,24 +151,10 @@ impl Responder for String {
|
|||
}
|
||||
}
|
||||
|
||||
/// Essentially aliases Stream<File>.
|
||||
impl Responder for File {
|
||||
fn respond<'a>(&mut self, mut res: FreshHyperResponse<'a>) -> Outcome<'a> {
|
||||
let size = match self.metadata() {
|
||||
Ok(md) => md.len(),
|
||||
Err(e) => {
|
||||
error_!("Failed to read file metadata: {:?}", e);
|
||||
return Forward((StatusCode::InternalServerError, res));
|
||||
}
|
||||
};
|
||||
|
||||
let mut v = Vec::new();
|
||||
if let Err(e) = self.read_to_end(&mut v) {
|
||||
error_!("Failed to read file: {:?}", e);
|
||||
return Forward((StatusCode::InternalServerError, res));
|
||||
}
|
||||
|
||||
res.headers_mut().set(header::ContentLength(size));
|
||||
res.start().and_then(|mut stream| stream.write_all(&v)).into_outcome()
|
||||
fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> Outcome<'a> {
|
||||
Stream::from(self).respond(res)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ use outcome::{self, Outcome};
|
|||
use http::hyper::StatusCode;
|
||||
use response::{Responder, StatusResponse};
|
||||
|
||||
/// Type alias for the `Outcome` of a `Handler`.
|
||||
pub type Response<'a> = outcome::Outcome<Box<Responder + 'a>, StatusCode, Data>;
|
||||
|
||||
impl<'a> Response<'a> {
|
||||
|
|
|
@ -9,11 +9,30 @@ use outcome::Outcome::*;
|
|||
/// The default size of each chunk in the streamed response.
|
||||
pub const CHUNK_SIZE: usize = 4096;
|
||||
|
||||
pub struct Stream<T: Read>(Box<T>);
|
||||
/// Streams a response to a client from an arbitrary `Read`er type.
|
||||
///
|
||||
/// The client is sent a "chunked" response, where the chunk size is at most
|
||||
/// 4KiB. This means that at most 4KiB are stored in memory while the response
|
||||
/// is being sent. This type should be used when sending responses that are
|
||||
/// arbitrarily large in size, such as when streaming from a local socket.
|
||||
pub struct Stream<T: Read>(T);
|
||||
|
||||
impl<T: Read> Stream<T> {
|
||||
/// Create a new stream from the given `reader`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Stream a response from whatever is in `stdin`. Note: you probably
|
||||
/// shouldn't do this.
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::io;
|
||||
/// use rocket::response::Stream;
|
||||
///
|
||||
/// let response = Stream::from(io::stdin());
|
||||
/// ```
|
||||
pub fn from(reader: T) -> Stream<T> {
|
||||
Stream(Box::new(reader))
|
||||
Stream(reader)
|
||||
}
|
||||
|
||||
// pub fn chunked(mut self, size: usize) -> Self {
|
||||
|
@ -33,6 +52,14 @@ impl<T: Read + Debug> Debug for Stream<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Sends a response to the client using the "Chunked" transfer encoding. The
|
||||
/// maximum chunk size is 4KiB.
|
||||
///
|
||||
/// # Failure
|
||||
///
|
||||
/// If reading from the input stream fails at any point during the response, the
|
||||
/// response is abandoned, and the response ends abruptly. An error is printed
|
||||
/// to the console with an indication of what went wrong.
|
||||
impl<T: Read> Responder for Stream<T> {
|
||||
fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> Outcome<'a> {
|
||||
let mut stream = match res.start() {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use response::{Responder, Outcome};
|
||||
use http::hyper::{StatusCode, FreshHyperResponse};
|
||||
|
||||
/// Responds to the client using a wrapped `Responder` and a given
|
||||
/// `StatusCode`.
|
||||
#[derive(Debug)]
|
||||
pub struct StatusResponse<R: Responder> {
|
||||
status: StatusCode,
|
||||
|
@ -8,6 +10,16 @@ pub struct StatusResponse<R: Responder> {
|
|||
}
|
||||
|
||||
impl<R: Responder> StatusResponse<R> {
|
||||
/// Creates a response with the given status code and underyling responder.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::response::StatusResponse;
|
||||
/// use rocket::http::StatusCode;
|
||||
///
|
||||
/// let response = StatusResponse::new(StatusCode::ImATeapot, "Hi!");
|
||||
/// ```
|
||||
pub fn new(status: StatusCode, responder: R) -> StatusResponse<R> {
|
||||
StatusResponse {
|
||||
status: status,
|
||||
|
@ -16,6 +28,8 @@ impl<R: Responder> StatusResponse<R> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Sets the status code of the response and then delegates the remainder of the
|
||||
/// response to the wrapped responder.
|
||||
impl<R: Responder> Responder for StatusResponse<R> {
|
||||
fn respond<'b>(&mut self, mut res: FreshHyperResponse<'b>) -> Outcome<'b> {
|
||||
*(res.status_mut()) = self.status;
|
||||
|
|
Loading…
Reference in New Issue