Fixup forms documentation for new features.

This commit is contained in:
Sergio Benitez 2017-02-03 16:56:29 -08:00
parent aefa2f1494
commit 0e82eb0b31
8 changed files with 128 additions and 23 deletions

View File

@ -102,7 +102,7 @@
//! address = "0.0.0.0" //! address = "0.0.0.0"
//! ``` //! ```
//! //!
//! ## Environment Variables //! ### Environment Variables
//! //!
//! All configuration parameters, including extras, can be overridden through //! All configuration parameters, including extras, can be overridden through
//! environment variables. To override the configuration parameter `{param}`, //! environment variables. To override the configuration parameter `{param}`,

View File

@ -1,8 +1,6 @@
use memchr::memchr2; use memchr::memchr2;
/// Iterator over the key/value pairs of a given HTTP form string. You'll likely /// Iterator over the key/value pairs of a given HTTP form string.
/// want to use this if you're implementing [FromForm](trait.FromForm.html)
/// manually, for whatever reason, by iterating over the items in `form_string`.
/// ///
/// **Note:** The returned key/value pairs are _not_ URL decoded. To URL decode /// **Note:** The returned key/value pairs are _not_ URL decoded. To URL decode
/// the raw strings, use `String::from_form_value`: /// the raw strings, use `String::from_form_value`:
@ -21,6 +19,23 @@ use memchr::memchr2;
/// } /// }
/// ``` /// ```
/// ///
/// # Completion
///
/// The iterator keeps track of whether the form string was parsed to completion
/// to determine if the form string was malformed. The iterator can be queried
/// for completion via the [completed](#method.completed) method, which returns
/// `true` if the iterator parsed the entire string that was passed to it. The
/// iterator can also attempt to parse any remaining contents via
/// [exhausted](#method.exhausted); this method returns `true` if exhaustion
/// succeeded.
///
/// This iterator guarantees that all valid form strings are parsed to
/// completion. The iterator attempts to be lenient. In particular, it allows
/// the following oddball behavior:
///
/// * A single trailing `&` character is allowed.
/// * Empty values are allowed.
///
/// # Examples /// # Examples
/// ///
/// `FormItems` can be used directly as an iterator: /// `FormItems` can be used directly as an iterator:
@ -53,11 +68,71 @@ pub struct FormItems<'f> {
} }
impl<'f> FormItems<'f> { impl<'f> FormItems<'f> {
/// Returns `true` if the form string was parsed to completion. Returns
/// `false` otherwise. All valid form strings will parse to completion,
/// while invalid form strings will not.
///
/// # Example
///
/// A valid form string parses to completion:
///
/// ```rust
/// use rocket::request::FormItems;
///
/// let mut items = FormItems::from("a=b&c=d");
/// let key_values: Vec<_> = items.by_ref().collect();
///
/// assert_eq!(key_values.len(), 2);
/// assert_eq!(items.completed(), true);
/// ```
///
/// In invalid form string does not parse to completion:
///
/// ```rust
/// use rocket::request::FormItems;
///
/// let mut items = FormItems::from("a=b&=d");
/// let key_values: Vec<_> = items.by_ref().collect();
///
/// assert_eq!(key_values.len(), 1);
/// assert_eq!(items.completed(), false);
/// ```
#[inline] #[inline]
pub fn completed(&self) -> bool { pub fn completed(&self) -> bool {
self.next_index >= self.string.len() self.next_index >= self.string.len()
} }
/// Parses all remaining key/value pairs and returns `true` if parsing ran
/// to completion. All valid form strings will parse to completion, while
/// invalid form strings will not.
///
/// # Example
///
/// A valid form string can be exhausted:
///
/// ```rust
/// use rocket::request::FormItems;
///
/// let mut items = FormItems::from("a=b&c=d");
///
/// assert!(items.next().is_some());
/// assert_eq!(items.completed(), false);
/// assert_eq!(items.exhausted(), true);
/// assert_eq!(items.completed(), true);
/// ```
///
/// An invalid form string cannot be exhausted:
///
/// ```rust
/// use rocket::request::FormItems;
///
/// let mut items = FormItems::from("a=b&=d");
///
/// assert!(items.next().is_some());
/// assert_eq!(items.completed(), false);
/// assert_eq!(items.exhausted(), false);
/// assert_eq!(items.completed(), false);
/// ```
pub fn exhausted(&mut self) -> bool { pub fn exhausted(&mut self) -> bool {
while let Some(_) = self.next() { } while let Some(_) = self.next() { }
self.completed() self.completed()
@ -69,6 +144,28 @@ impl<'f> FormItems<'f> {
self.next_index = self.string.len() self.next_index = self.string.len()
} }
/// Retrieves the original string being parsed by this iterator. The string
/// returned by this method does not change, regardless of the status of the
/// iterator.
///
/// # Example
///
/// ```rust
/// use rocket::request::FormItems;
///
/// let form_string = "a=b&c=d";
/// let mut items = FormItems::from(form_string);
/// assert_eq!(items.inner_str(), form_string);
///
/// assert!(items.next().is_some());
/// assert_eq!(items.inner_str(), form_string);
///
/// assert!(items.next().is_some());
/// assert_eq!(items.inner_str(), form_string);
///
/// assert!(items.next().is_none());
/// assert_eq!(items.inner_str(), form_string);
/// ```
#[inline] #[inline]
pub fn inner_str(&self) -> &'f str { pub fn inner_str(&self) -> &'f str {
self.string self.string
@ -76,6 +173,8 @@ impl<'f> FormItems<'f> {
} }
impl<'f> From<&'f str> for FormItems<'f> { impl<'f> From<&'f str> for FormItems<'f> {
/// Returns an iterator over the key/value pairs in the
/// `x-www-form-urlencoded` form `string`.
fn from(string: &'f str) -> FormItems<'f> { fn from(string: &'f str) -> FormItems<'f> {
FormItems { FormItems {
string: string, string: string,
@ -159,6 +258,7 @@ mod test {
check_form!("a=b&a=", &[("a", "b"), ("a", "")]); check_form!("a=b&a=", &[("a", "b"), ("a", "")]);
check_form!(@bad "user=&password"); check_form!(@bad "user=&password");
check_form!(@bad "user=x&&");
check_form!(@bad "a=b&a"); check_form!(@bad "a=b&a");
check_form!(@bad "="); check_form!(@bad "=");
check_form!(@bad "&"); check_form!(@bad "&");

View File

@ -1,8 +1,7 @@
use request::FormItems; use request::FormItems;
/// Trait to create an instance of some type from an HTTP form. The /// Trait to create an instance of some type from an HTTP form.
/// [Form](struct.Form.html) type requires that its generic parameter implements /// [Form](struct.Form.html) requires its generic type to implement this trait.
/// this trait.
/// ///
/// This trait can be automatically derived via the /// This trait can be automatically derived via the
/// [rocket_codegen](/rocket_codegen) plugin: /// [rocket_codegen](/rocket_codegen) plugin:
@ -44,17 +43,16 @@ use request::FormItems;
/// ///
/// # Implementing /// # Implementing
/// ///
/// If you implement `FormForm` yourself, use the /// An implementation of `FromForm` uses the [FormItems](struct.FormItems.html)
/// [FormItems](struct.FormItems.html) iterator to iterate through the form /// iterator to iterate through the raw form key/value pairs. Be aware that form
/// key/value pairs. Be aware that form fields that are typically hidden from /// fields that are typically hidden from your application, such as `_method`,
/// your application, such as `_method`, will be present while iterating. /// will be present while iterating.
pub trait FromForm<'f>: Sized { pub trait FromForm<'f>: Sized {
/// The associated error to be returned when parsing fails. /// The associated error to be returned when parsing fails.
type Error; type Error;
/// Parses an instance of `Self` from a raw HTTP form string /// Parses an instance of `Self` from the form items or returns an `Error`
/// (`application/x-www-form-urlencoded data`) or returns an `Error` if one /// if one cannot be parsed.
/// cannot be parsed.
fn from_form_items(form_items: &mut FormItems<'f>) -> Result<Self, Self::Error>; fn from_form_items(form_items: &mut FormItems<'f>) -> Result<Self, Self::Error>;
} }

View File

@ -37,7 +37,7 @@ use outcome::Outcome::*;
/// A `FromData` type for parsing `FromForm` types. /// A `FromData` type for parsing `FromForm` types.
/// ///
/// This type implements the `FromData` trait. It provides a generic means to /// This type implements the `FromData` trait. It provides a generic means to
/// parse arbitrary structure from incoming form data. /// parse arbitrary structures from incoming form data.
/// ///
/// # Usage /// # Usage
/// ///
@ -251,12 +251,17 @@ impl<'f, T: FromForm<'f> + Debug + 'f> Debug for Form<'f, T> {
/// ///
/// If the content type of the request data is not /// If the content type of the request data is not
/// `application/x-www-form-urlencoded`, `Forward`s the request. If the form /// `application/x-www-form-urlencoded`, `Forward`s the request. If the form
/// data cannot be parsed into a `T` or reading the incoming stream failed, /// data cannot be parsed into a `T`, a `Failure` with status code
/// returns a `Failure` with the raw form string (if avaialble). /// `UnprocessableEntity` is returned. If the form string is malformed, a
/// `Failure` with status code `BadRequest` is returned. Finally, if reading the
/// incoming stream fails, returns a `Failure` with status code
/// `InternalServerError`. In all failure cases, the raw form string is returned
/// if it was able to be retrieved from the incoming stream.
/// ///
/// All relevant warnings and errors are written to the console in Rocket /// All relevant warnings and errors are written to the console in Rocket
/// logging format. /// logging format.
impl<'f, T: FromForm<'f>> FromData for Form<'f, T> where T::Error: Debug { impl<'f, T: FromForm<'f>> FromData for Form<'f, T> where T::Error: Debug {
/// The raw form string, if it was able to be retrieved from the request.
type Error = Option<String>; type Error = Option<String>;
fn from_data(request: &Request, data: Data) -> data::Outcome<Self, Self::Error> { fn from_data(request: &Request, data: Data) -> data::Outcome<Self, Self::Error> {

View File

@ -71,7 +71,7 @@ impl<S, E> IntoOutcome<S, (Status, E), ()> for Result<S, E> {
/// Rocket implements `FromRequest` for several built-in types. Their behavior /// Rocket implements `FromRequest` for several built-in types. Their behavior
/// is documented here. /// is documented here.
/// ///
/// * **URI** /// * **&URI**
/// ///
/// Extracts the [URI](/rocket/http/uri/struct.URI.html) from the incoming /// Extracts the [URI](/rocket/http/uri/struct.URI.html) from the incoming
/// request. /// request.

View File

@ -12,5 +12,6 @@ pub use self::param::{FromParam, FromSegments};
pub use self::form::{Form, FromForm, FromFormValue, FormItems}; pub use self::form::{Form, FromForm, FromFormValue, FormItems};
pub use self::state::State; pub use self::state::State;
/// Type alias to retrieve flash messages from a request. /// Type alias to retrieve [Flash](/rocket/response/struct.Flash.html) messages
/// from a request.
pub type FlashMessage = ::response::Flash<()>; pub type FlashMessage = ::response::Flash<()>;

View File

@ -20,7 +20,7 @@ use http::hyper;
/// ///
/// This should be used sparingly in Rocket applications. In particular, it /// This should be used sparingly in Rocket applications. In particular, it
/// should likely only be used when writing /// should likely only be used when writing
/// [FromRequest](/rocket/request/struct.Request.html) implementations. It /// [FromRequest](/rocket/request/trait.FromRequest.html) implementations. It
/// contains all of the information for a given web request except for the body /// contains all of the information for a given web request except for the body
/// data. This includes the HTTP method, URI, cookies, headers, and more. /// data. This includes the HTTP method, URI, cookies, headers, and more.
pub struct Request<'r> { pub struct Request<'r> {
@ -319,7 +319,8 @@ impl<'r> Request<'r> {
*self.params.borrow_mut() = route.get_param_indexes(self.uri()); *self.params.borrow_mut() = route.get_param_indexes(self.uri());
} }
/// Get the `n`th path parameter as a string, if it exists. /// Get the `n`th path parameter as a string, if it exists. This is used by
/// codegen.
#[doc(hidden)] #[doc(hidden)]
pub fn get_param_str(&self, n: usize) -> Option<&str> { pub fn get_param_str(&self, n: usize) -> Option<&str> {
let params = self.params.borrow(); let params = self.params.borrow();
@ -364,7 +365,7 @@ impl<'r> Request<'r> {
} }
/// Get the segments beginning at the `n`th dynamic parameter, if they /// Get the segments beginning at the `n`th dynamic parameter, if they
/// exist. /// exist. Used by codegen.
#[doc(hidden)] #[doc(hidden)]
pub fn get_raw_segments(&self, n: usize) -> Option<Segments> { pub fn get_raw_segments(&self, n: usize) -> Option<Segments> {
let params = self.params.borrow(); let params = self.params.borrow();

View File

@ -11,7 +11,7 @@ use http::Status;
/// number of handlers. A value for the given type must previously have been /// number of handlers. A value for the given type must previously have been
/// registered to be managed by Rocket via the /// registered to be managed by Rocket via the
/// [manage](/rocket/struct.Rocket.html#method.manage) method. The type being /// [manage](/rocket/struct.Rocket.html#method.manage) method. The type being
/// managed must be thread safe and sendable across thread boundaries. In otehr /// managed must be thread safe and sendable across thread boundaries. In other
/// words, it must implement `Send + Sync + 'static`. /// words, it must implement `Send + Sync + 'static`.
/// ///
/// # Example /// # Example