Ignore _method field in derived FromForm.

Fixes #45.
This commit is contained in:
Sergio Benitez 2016-12-25 21:32:32 -06:00
parent f86b1cd775
commit 71419933a5
5 changed files with 79 additions and 6 deletions

View File

@ -191,6 +191,11 @@ fn from_form_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substruct
for (k, v) in ::rocket::request::FormItems($arg) { for (k, v) in ::rocket::request::FormItems($arg) {
match k { match k {
$arms $arms
field if field == "_method" => {
/* This is a Rocket-specific field. If the user hasn't asked
* for it, just let it go by without error. This should stay
* in sync with Rocket::preprocess. */
}
_ => { _ => {
println!(" => {}={} has no matching field in struct.", println!(" => {}={} has no matching field in struct.",
k, v); k, v);

View File

@ -47,6 +47,12 @@ struct DefaultInput<'r> {
arg: Option<&'r str>, arg: Option<&'r str>,
} }
#[derive(Debug, PartialEq, FromForm)]
struct ManualMethod<'r> {
_method: Option<&'r str>,
done: bool
}
fn main() { fn main() {
// Same number of arguments: simple case. // Same number of arguments: simple case.
let task = TodoTask::from_form_string("description=Hello&completed=on"); let task = TodoTask::from_form_string("description=Hello&completed=on");
@ -59,6 +65,13 @@ fn main() {
let task = TodoTask::from_form_string("other=a&description=Hello&completed=on"); let task = TodoTask::from_form_string("other=a&description=Hello&completed=on");
assert!(task.is_err()); assert!(task.is_err());
// Ensure _method isn't required.
let task = TodoTask::from_form_string("_method=patch&description=Hello&completed=off");
assert_eq!(task, Ok(TodoTask {
description: "Hello".to_string(),
completed: false
}));
let form_string = &[ let form_string = &[
"password=testing", "checkbox=off", "checkbox=on", "number=10", "password=testing", "checkbox=off", "checkbox=on", "number=10",
"checkbox=off", "textarea=", "select=a", "radio=c", "checkbox=off", "textarea=", "select=a", "radio=c",
@ -79,4 +92,18 @@ fn main() {
assert_eq!(default, Ok(DefaultInput { assert_eq!(default, Ok(DefaultInput {
arg: None arg: None
})); }));
// Ensure _method can be captured if desired.
let manual = ManualMethod::from_form_string("_method=put&done=true");
assert_eq!(manual, Ok(ManualMethod {
_method: Some("put"),
done: true
}));
// And ignored when not present.
let manual = ManualMethod::from_form_string("done=true");
assert_eq!(manual, Ok(ManualMethod {
_method: None,
done: true
}));
} }

View File

@ -38,9 +38,14 @@ use error::Error;
/// ``` /// ```
/// ///
/// When deriving `FromForm`, every field in the structure must implement /// When deriving `FromForm`, every field in the structure must implement
/// [FromFormValue](trait.FromFormValue.html). If you implement `FormForm` /// [FromFormValue](trait.FromFormValue.html).
/// yourself, use the [FormItems](struct.FormItems.html) iterator to iterate ///
/// through the form key/value pairs. /// # Implementing
///
/// If you implement `FormForm` yourself, use the
/// [FormItems](struct.FormItems.html) iterator to iterate through the form
/// key/value pairs. Be aware that form fields that are typically hidden from
/// your application, such as `_method`, 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;

View File

@ -75,7 +75,7 @@ impl Rocket {
#[inline] #[inline]
fn issue_response(&self, mut response: Response, hyp_res: hyper::FreshResponse) { fn issue_response(&self, mut response: Response, hyp_res: hyper::FreshResponse) {
// Add the 'rocket' server header, and write out the response. // Add the 'rocket' server header, and write out the response.
// TODO: If removing Hyper, write out `Data` header too. // TODO: If removing Hyper, write out `Date` header too.
response.set_header(header::Server("rocket".to_string())); response.set_header(header::Server("rocket".to_string()));
match self.write_response(response, hyp_res) { match self.write_response(response, hyp_res) {
@ -134,7 +134,8 @@ impl Rocket {
} }
/// Preprocess the request for Rocket-specific things. At this time, we're /// Preprocess the request for Rocket-specific things. At this time, we're
/// only checking for _method in forms. /// only checking for _method in forms. Keep this in-sync with derive_form
/// when preprocessing form fields.
fn preprocess_request(&self, req: &mut Request, data: &Data) { fn preprocess_request(&self, req: &mut Request, data: &Data) {
// Check if this is a form and if the form contains the special _method // Check if this is a form and if the form contains the special _method
// field which we use to reinterpret the request's method. // field which we use to reinterpret the request's method.
@ -176,7 +177,7 @@ impl Rocket {
// convince it to give us another mutable reference. // convince it to give us another mutable reference.
// FIXME: Pay the cost to copy Request into UnsafeCell? Pay the // FIXME: Pay the cost to copy Request into UnsafeCell? Pay the
// cost to use RefCell? Move the call to `issue_response` here // cost to use RefCell? Move the call to `issue_response` here
// to move Request and move directly into a RefCell? // to move Request and move directly into an UnsafeCell?
let request: &'r mut Request = unsafe { let request: &'r mut Request = unsafe {
&mut *(request as *const Request as *mut Request) &mut *(request as *const Request as *mut Request)
}; };

View File

@ -0,0 +1,35 @@
#![feature(plugin, custom_derive)]
#![plugin(rocket_codegen)]
extern crate rocket;
use rocket::request::Form;
#[derive(FromForm)]
struct FormData {
form_data: String,
}
#[patch("/", data = "<form_data>")]
fn bug(form_data: Form<FormData>) -> &'static str {
assert_eq!("Form data", &form_data.get().form_data);
"OK"
}
use rocket::testing::MockRequest;
use rocket::http::Method::*;
use rocket::http::ContentType;
#[test]
fn method_eval() {
let rocket = rocket::ignite().mount("/", routes![bug]);
let mut req = MockRequest::new(Patch, "/")
.header(ContentType::Form)
.body("_method=patch&form_data=Form+data");
let mut response = req.dispatch_with(&rocket);
let body_str = response.body().and_then(|b| b.into_string());
assert_eq!(body_str, Some("OK".to_string()));
}