Add request preprocessing for _method in forms.

resolves #12
This commit is contained in:
Sergio Benitez 2016-09-25 02:26:15 -07:00
parent 31cca896f4
commit cd4af6836a
9 changed files with 92 additions and 18 deletions

View File

@ -105,7 +105,6 @@ impl RouteGenerateExt for RouteParams {
let param = self.query_param.as_ref();
let expr = quote_expr!(ecx,
match _req.uri().query() {
// FIXME: Don't reinterpret as UTF8 again.
Some(query) => query,
None => return ::rocket::Response::forward()
}

View File

@ -42,8 +42,8 @@ pub fn plugin_registrar(reg: &mut Registry) {
register_decorators!(reg,
"derive_FromForm" => from_form_derive,
"error" => error_decorator,
"error" => error_decorator,
"route" => route_decorator,
"get" => get_decorator,
"put" => put_decorator,

View File

@ -40,7 +40,7 @@ fn new(todo: Task) -> Result<Flash<Redirect>, Template> {
}
// Should likely do something to simulate PUT.
#[get("/<id>/toggle")]
#[put("/<id>")]
fn toggle(id: i32) -> Result<Redirect, Template> {
if Task::toggle_with_id(id) {
Ok(Redirect::to("/"))
@ -50,7 +50,7 @@ fn toggle(id: i32) -> Result<Redirect, Template> {
}
// Should likely do something to simulate DELETE.
#[get("/<id>/delete")]
#[delete("/<id>")]
fn delete(id: i32) -> Result<Flash<Redirect>, Template> {
if Task::delete_with_id(id) {
Ok(Flash::success(Redirect::to("/"), "Todo was deleted."))

View File

@ -202,6 +202,7 @@ input[type="button"]:focus {
outline: 0; }
.button.button-primary,
button.button-primary,
button.primary,
input[type="submit"].button-primary,
input[type="reset"].button-primary,
input[type="button"].button-primary {
@ -210,11 +211,13 @@ input[type="button"].button-primary {
border-color: #33C3F0; }
.button.button-primary:hover,
button.button-primary:hover,
button.primary:hover,
input[type="submit"].button-primary:hover,
input[type="reset"].button-primary:hover,
input[type="button"].button-primary:hover,
.button.button-primary:focus,
button.button-primary:focus,
button.primary:focus,
input[type="submit"].button-primary:focus,
input[type="reset"].button-primary:focus,
input[type="button"].button-primary:focus {

View File

@ -21,3 +21,38 @@
span.completed {
text-decoration: line-through;
}
form.inline {
display: inline;
}
form.link,
button.link {
display: inline;
color: #1EAEDB;
border: none;
outline: none;
background: none;
cursor: pointer;
padding: 0;
margin: 0 0 0 0;
height: inherit;
text-decoration: underline;
font-size: inherit;
text-transform: none;
font-weight: normal;
line-height: inherit;
letter-spacing: inherit;
}
form.link:hover, button.link:hover {
color: #0FA0CE;
}
button.small {
height: 20px;
padding: 0 10px;
font-size: 10px;
line-height: 20px;
margin: 0 2.5px;
}

View File

@ -19,7 +19,7 @@
<div class="row">
<h4>Rocket Todo</h4>
<form action="/todo" method="post" accept-charset="utf-8">
<form action="/todo" method="post">
<div class="ten columns">
<input type="text" placeholder="enter a task description..."
name="description" id="description" value="" autofocus
@ -43,12 +43,21 @@
{% if task.completed %}
<li>
<span class="completed">{{ task.description }}</span>
<a href="/todo/{{ task.id }}/toggle">undo</a>
<a href="/todo/{{ task.id }}/delete">delete</a>
<form class="inline" action="/todo/{{task.id}}" method="post">
<input type="hidden" name="_method" value="put" />
<button class="small" type="submit">undo</button>
</form>
<form class="inline" action="/todo/{{task.id}}" method="post">
<input type="hidden" name="_method" value="delete" />
<button class="primary small" type="submit">delete</button>
</form>
</li>
{% else %}
<li>
<a href="/todo/{{ task.id }}/toggle">{{ task.description }}</a>
<form class="link" action="/todo/{{task.id}}" method="post">
<input type="hidden" name="_method" value="put" />
<button class="link" type="submit">{{ task.description }}</button>
</form>
</li>
{% endif %}
{% endfor %}

View File

@ -49,6 +49,8 @@ impl ContentType {
is_some!(is_xml: Application/Xml);
is_some!(is_any: Star/Star);
is_some!(is_html: Application/Html);
is_some!(is_form: Application/WwwFormUrlEncoded);
is_some!(is_data: Multipart/FormData);
}
impl Default for ContentType {

View File

@ -48,15 +48,15 @@ impl FromStr for Method {
fn from_str(s: &str) -> Result<Method, Error> {
match s {
"GET" => Ok(Get),
"PUT" => Ok(Put),
"POST" => Ok(Post),
"DELETE" => Ok(Delete),
"OPTIONS" => Ok(Options),
"HEAD" => Ok(Head),
"TRACE" => Ok(Trace),
"CONNECT" => Ok(Connect),
"PATCH" => Ok(Patch),
"GET" | "get" => Ok(Get),
"PUT" | "put" => Ok(Put),
"POST" | "post" => Ok(Post),
"DELETE" | "delete" => Ok(Delete),
"OPTIONS" | "options" => Ok(Options),
"HEAD" | "head" => Ok(Head),
"TRACE" | "trace" => Ok(Trace),
"CONNECT" | "connect" => Ok(Connect),
"PATCH" | "patch" => Ok(Patch),
_ => Err(Error::BadMethod),
}
}

View File

@ -4,6 +4,8 @@ use request::HyperRequest;
use catcher;
use std::collections::HashMap;
use std::str::from_utf8_unchecked;
use std::cmp::min;
use term_painter::Color::*;
use term_painter::ToStyle;
@ -36,7 +38,10 @@ impl Rocket {
// Try to create a Rocket request from the hyper request.
let request = match Request::from_hyp(hyp_req) {
Ok(req) => req,
Ok(mut req) => {
self.preprocess_request(&mut req);
req
},
Err(ref reason) => {
let mock_request = Request::mock(Method::Get, uri.as_str());
debug_!("Bad request: {}", reason);
@ -74,6 +79,27 @@ impl Rocket {
self.handle_not_found(&request, res);
}
/// Preprocess the request for Rocket-specific things. At this time, we're
/// only checking for _method in forms.
fn preprocess_request(&self, req: &mut Request) {
// Check if this is a form and if the form contains the special _method
// field which we use to reinterpret the request's method.
let data_len = req.data.len();
let (min_len, max_len) = ("_method=get".len(), "_method=delete".len());
if req.content_type().is_form() && data_len >= min_len {
let form = unsafe {
from_utf8_unchecked(&req.data.as_slice()[..min(data_len, max_len)])
};
let mut form_items = form::FormItems(form);
if let Some(("_method", value)) = form_items.next() {
if let Ok(method) = value.parse() {
req.method = method;
}
}
}
}
// Call on internal server error.
fn handle_internal_error<'r>(&self, request: &'r Request<'r>,
response: FreshHyperResponse) {