diff --git a/contrib/sync_db_pools/lib/src/connection.rs b/contrib/sync_db_pools/lib/src/connection.rs index c395dc28..73c6913b 100644 --- a/contrib/sync_db_pools/lib/src/connection.rs +++ b/contrib/sync_db_pools/lib/src/connection.rs @@ -210,7 +210,7 @@ impl<'r, K: 'static, C: Poolable> FromRequest<'r> for Connection { #[inline] async fn from_request(request: &'r Request<'_>) -> Outcome { match request.rocket().state::>() { - Some(c) => c.get().await.into_outcome((Status::ServiceUnavailable, ())), + Some(c) => c.get().await.or_error((Status::ServiceUnavailable, ())), None => { error_!("Missing database fairing for `{}`", std::any::type_name::()); Outcome::Error((Status::InternalServerError, ())) diff --git a/contrib/ws/src/websocket.rs b/contrib/ws/src/websocket.rs index 6ad295ac..32b598e8 100644 --- a/contrib/ws/src/websocket.rs +++ b/contrib/ws/src/websocket.rs @@ -30,7 +30,7 @@ use crate::result::{Result, Error}; /// ### Forwarding /// /// If the incoming request is not a valid WebSocket request, the guard -/// forwards. The guard never fails. +/// forwards with a status of `BadRequest`. The guard never fails. pub struct WebSocket { config: Config, key: String, @@ -203,7 +203,7 @@ impl<'r> FromRequest<'r> for WebSocket { let key = headers.get_one("Sec-WebSocket-Key").map(|k| derive_accept_key(k.as_bytes())); match key { Some(key) if is_upgrade && is_ws && is_13 => Outcome::Success(WebSocket::new(key)), - Some(_) | None => Outcome::Forward(Status::NotFound) + Some(_) | None => Outcome::Forward(Status::BadRequest) } } } diff --git a/core/codegen/src/attribute/route/mod.rs b/core/codegen/src/attribute/route/mod.rs index e30117db..c2e3043a 100644 --- a/core/codegen/src/attribute/route/mod.rs +++ b/core/codegen/src/attribute/route/mod.rs @@ -105,7 +105,7 @@ fn query_decls(route: &Route) -> Option { if !__e.is_empty() { #_log::warn_!("Query string failed to match route declaration."); for _err in __e { #_log::warn_!("{}", _err); } - return #Outcome::Forward((#__data, #Status::NotFound)); + return #Outcome::Forward((#__data, #Status::UnprocessableEntity)); } (#(#ident.unwrap()),*) @@ -146,7 +146,7 @@ fn param_guard_decl(guard: &Guard) -> TokenStream { #_log::warn_!("Parameter guard `{}: {}` is forwarding: {:?}.", #name, stringify!(#ty), __error); - #Outcome::Forward((#__data, #Status::NotFound)) + #Outcome::Forward((#__data, #Status::UnprocessableEntity)) }); // All dynamic parameters should be found if this function is being called; diff --git a/core/codegen/tests/route.rs b/core/codegen/tests/route.rs index d8e45094..de65f2fa 100644 --- a/core/codegen/tests/route.rs +++ b/core/codegen/tests/route.rs @@ -269,7 +269,7 @@ fn test_query_collection() { let colors = &["red"]; let dog = &["name=Fido"]; - assert_eq!(run(&client, colors, dog).0, Status::NotFound); + assert_eq!(run(&client, colors, dog).0, Status::UnprocessableEntity); let colors = &["red"]; let dog = &["name=Fido", "age=2"]; diff --git a/core/lib/src/data/data.rs b/core/lib/src/data/data.rs index 3f393c55..bbd2bb75 100644 --- a/core/lib/src/data/data.rs +++ b/core/lib/src/data/data.rs @@ -112,7 +112,7 @@ impl<'r> Data<'r> { /// /// async fn from_data(r: &'r Request<'_>, mut data: Data<'r>) -> Outcome<'r, Self> { /// if data.peek(2).await != b"hi" { - /// return Outcome::Forward((data, Status::NotFound)) + /// return Outcome::Forward((data, Status::BadRequest)) /// } /// /// /* .. */ diff --git a/core/lib/src/data/from_data.rs b/core/lib/src/data/from_data.rs index b3ac98c8..3eec2893 100644 --- a/core/lib/src/data/from_data.rs +++ b/core/lib/src/data/from_data.rs @@ -9,27 +9,6 @@ use crate::outcome::{self, IntoOutcome, try_outcome, Outcome::*}; pub type Outcome<'r, T, E = >::Error> = outcome::Outcome, Status)>; -impl<'r, S, E> IntoOutcome, Status)> for Result { - type Error = Status; - type Forward = (Data<'r>, Status); - - #[inline] - fn into_outcome(self, status: Status) -> Outcome<'r, S, E> { - match self { - Ok(val) => Success(val), - Err(err) => Error((status, err)) - } - } - - #[inline] - fn or_forward(self, (data, status): (Data<'r>, Status)) -> Outcome<'r, S, E> { - match self { - Ok(val) => Success(val), - Err(_) => Forward((data, status)) - } - } -} - /// Trait implemented by data guards to derive a value from request body data. /// /// # Data Guards @@ -271,7 +250,7 @@ impl<'r, S, E> IntoOutcome, Status)> for Result /// // Ensure the content type is correct before opening the data. /// let person_ct = ContentType::new("application", "x-person"); /// if req.content_type() != Some(&person_ct) { -/// return Outcome::Forward((data, Status::NotFound)); +/// return Outcome::Forward((data, Status::UnsupportedMediaType)); /// } /// /// // Use a configured limit with name 'person' or fallback to default. @@ -343,7 +322,7 @@ impl<'r> FromData<'r> for Capped { async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> { let limit = req.limits().get("string").unwrap_or(Limits::STRING); - data.open(limit).into_string().await.into_outcome(Status::BadRequest) + data.open(limit).into_string().await.or_error(Status::BadRequest) } } @@ -406,7 +385,7 @@ impl<'r> FromData<'r> for Capped> { async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> { let limit = req.limits().get("bytes").unwrap_or(Limits::BYTES); - data.open(limit).into_bytes().await.into_outcome(Status::BadRequest) + data.open(limit).into_bytes().await.or_error(Status::BadRequest) } } diff --git a/core/lib/src/form/parser.rs b/core/lib/src/form/parser.rs index 7a9c4437..b53e2a50 100644 --- a/core/lib/src/form/parser.rs +++ b/core/lib/src/form/parser.rs @@ -35,7 +35,7 @@ impl<'r, 'i> Parser<'r, 'i> { let parser = match req.content_type() { Some(c) if c.is_form() => Self::from_form(req, data).await, Some(c) if c.is_form_data() => Self::from_multipart(req, data).await, - _ => return Outcome::Forward((data, Status::NotFound)), + _ => return Outcome::Forward((data, Status::UnsupportedMediaType)), }; match parser { diff --git a/core/lib/src/fs/server.rs b/core/lib/src/fs/server.rs index 8aac5c25..45300db5 100644 --- a/core/lib/src/fs/server.rs +++ b/core/lib/src/fs/server.rs @@ -1,9 +1,10 @@ use std::path::{PathBuf, Path}; use crate::{Request, Data}; -use crate::http::{Method, uri::Segments, ext::IntoOwned}; +use crate::http::{Method, Status, uri::Segments, ext::IntoOwned}; use crate::route::{Route, Handler, Outcome}; -use crate::response::Redirect; +use crate::response::{Redirect, Responder}; +use crate::outcome::IntoOutcome; use crate::fs::NamedFile; /// Custom handler for serving static files. @@ -203,10 +204,10 @@ impl Handler for FileServer { }; if segments.is_empty() { - let file = NamedFile::open(&self.root).await.ok(); - return Outcome::from_or_forward(req, data, file); + let file = NamedFile::open(&self.root).await; + return file.respond_to(req).or_forward((data, Status::NotFound)); } else { - return Outcome::forward(data); + return Outcome::forward(data, Status::NotFound); } } @@ -224,18 +225,23 @@ impl Handler for FileServer { .expect("adding a trailing slash to a known good path => valid path") .into_owned(); - return Outcome::from_or_forward(req, data, Redirect::permanent(normal)); + return Redirect::permanent(normal) + .respond_to(req) + .or_forward((data, Status::InternalServerError)); } if !options.contains(Options::Index) { - return Outcome::forward(data); + return Outcome::forward(data, Status::NotFound); } - let index = NamedFile::open(p.join("index.html")).await.ok(); - Outcome::from_or_forward(req, data, index) + let index = NamedFile::open(p.join("index.html")).await; + index.respond_to(req).or_forward((data, Status::NotFound)) }, - Some(p) => Outcome::from_or_forward(req, data, NamedFile::open(p).await.ok()), - None => Outcome::forward(data), + Some(p) => { + let file = NamedFile::open(p).await; + file.respond_to(req).or_forward((data, Status::NotFound)) + } + None => Outcome::forward(data, Status::NotFound), } } } diff --git a/core/lib/src/fs/temp_file.rs b/core/lib/src/fs/temp_file.rs index 0f0ca9f5..fe969e7e 100644 --- a/core/lib/src/fs/temp_file.rs +++ b/core/lib/src/fs/temp_file.rs @@ -543,7 +543,7 @@ impl<'r> FromData<'r> for Capped> { } TempFile::from(req, data, None, req.content_type().cloned()).await - .into_outcome(Status::BadRequest) + .or_error(Status::BadRequest) } } diff --git a/core/lib/src/mtls.rs b/core/lib/src/mtls.rs index 07367bcf..5910bb52 100644 --- a/core/lib/src/mtls.rs +++ b/core/lib/src/mtls.rs @@ -20,6 +20,6 @@ impl<'r> FromRequest<'r> for Certificate<'r> { async fn from_request(req: &'r Request<'_>) -> Outcome { let certs = req.connection.client_certificates.as_ref().or_forward(Status::Unauthorized); let data = try_outcome!(try_outcome!(certs).chain_data().or_forward(Status::Unauthorized)); - Certificate::parse(data).into_outcome(Status::Unauthorized) + Certificate::parse(data).or_error(Status::Unauthorized) } } diff --git a/core/lib/src/outcome.rs b/core/lib/src/outcome.rs index 549e8509..88828b36 100644 --- a/core/lib/src/outcome.rs +++ b/core/lib/src/outcome.rs @@ -90,6 +90,10 @@ use std::fmt; use yansi::{Paint, Color}; +use crate::{route, request, response}; +use crate::data::{self, Data, FromData}; +use crate::http::Status; + use self::Outcome::*; /// An enum representing success (`Success`), error (`Error`), or forwarding @@ -107,46 +111,6 @@ pub enum Outcome { Forward(F), } -/// Conversion trait from some type into an Outcome type. -pub trait IntoOutcome { - /// The type to use when returning an `Outcome::Error`. - type Error: Sized; - - /// The type to use when returning an `Outcome::Forward`. - type Forward: Sized; - - /// Converts `self` into an `Outcome`. If `self` represents a success, an - /// `Outcome::Success` is returned. Otherwise, an `Outcome::Error` is - /// returned with `error` as the inner value. - fn into_outcome(self, error: Self::Error) -> Outcome; - - /// Converts `self` into an `Outcome`. If `self` represents a success, an - /// `Outcome::Success` is returned. Otherwise, an `Outcome::Forward` is - /// returned with `forward` as the inner value. - fn or_forward(self, forward: Self::Forward) -> Outcome; -} - -impl IntoOutcome for Option { - type Error = E; - type Forward = F; - - #[inline] - fn into_outcome(self, error: E) -> Outcome { - match self { - Some(val) => Success(val), - None => Error(error) - } - } - - #[inline] - fn or_forward(self, forward: F) -> Outcome { - match self { - Some(val) => Success(val), - None => Forward(forward) - } - } -} - impl Outcome { /// Unwraps the Outcome, yielding the contents of a Success. /// @@ -651,15 +615,6 @@ impl Outcome { Outcome::Forward(v) => Err(v), } } - - #[inline] - fn formatting(&self) -> (Color, &'static str) { - match *self { - Success(..) => (Color::Green, "Success"), - Error(..) => (Color::Red, "Error"), - Forward(..) => (Color::Yellow, "Forward"), - } - } } impl<'a, S: Send + 'a, E: Send + 'a, F: Send + 'a> Outcome { @@ -755,15 +710,158 @@ crate::export! { } } +impl Outcome { + #[inline] + fn dbg_str(&self) -> &'static str { + match self { + Success(..) => "Success", + Error(..) => "Error", + Forward(..) => "Forward", + } + } + + #[inline] + fn color(&self) -> Color { + match self { + Success(..) => Color::Green, + Error(..) => Color::Red, + Forward(..) => Color::Yellow, + } + } +} + +pub(crate) struct Display<'a, 'r>(&'a route::Outcome<'r>); + +impl<'r> route::Outcome<'r> { + pub(crate) fn log_display(&self) -> Display<'_, 'r> { + impl fmt::Display for Display<'_, '_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", "Outcome: ".primary().bold())?; + + let color = self.0.color(); + match self.0 { + Success(r) => write!(f, "{}({})", "Success".paint(color), r.status().primary()), + Error(s) => write!(f, "{}({})", "Error".paint(color), s.primary()), + Forward((_, s)) => write!(f, "{}({})", "Forward".paint(color), s.primary()), + } + } + } + + Display(self) + } +} + impl fmt::Debug for Outcome { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Outcome::{}", self.formatting().1) + write!(f, "Outcome::{}", self.dbg_str()) } } impl fmt::Display for Outcome { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let (color, string) = self.formatting(); - write!(f, "{}", string.paint(color)) + write!(f, "{}", self.dbg_str().paint(self.color())) + } +} + +/// Conversion trait from some type into an Outcome type. +pub trait IntoOutcome { + /// The type to use when returning an `Outcome::Error`. + type Error: Sized; + + /// The type to use when returning an `Outcome::Forward`. + type Forward: Sized; + + /// Converts `self` into an `Outcome`. If `self` represents a success, an + /// `Outcome::Success` is returned. Otherwise, an `Outcome::Error` is + /// returned with `error` as the inner value. + fn or_error(self, error: Self::Error) -> Outcome; + + /// Converts `self` into an `Outcome`. If `self` represents a success, an + /// `Outcome::Success` is returned. Otherwise, an `Outcome::Forward` is + /// returned with `forward` as the inner value. + fn or_forward(self, forward: Self::Forward) -> Outcome; +} + +impl IntoOutcome> for Option { + type Error = E; + type Forward = F; + + #[inline] + fn or_error(self, error: E) -> Outcome { + match self { + Some(val) => Success(val), + None => Error(error) + } + } + + #[inline] + fn or_forward(self, forward: F) -> Outcome { + match self { + Some(val) => Success(val), + None => Forward(forward) + } + } +} + +impl<'r, T: FromData<'r>> IntoOutcome> for Result { + type Error = Status; + type Forward = (Data<'r>, Status); + + #[inline] + fn or_error(self, error: Status) -> data::Outcome<'r, T> { + match self { + Ok(val) => Success(val), + Err(err) => Error((error, err)) + } + } + + #[inline] + fn or_forward(self, (data, forward): (Data<'r>, Status)) -> data::Outcome<'r, T> { + match self { + Ok(val) => Success(val), + Err(_) => Forward((data, forward)) + } + } +} + +impl IntoOutcome> for Result { + type Error = Status; + type Forward = Status; + + #[inline] + fn or_error(self, error: Status) -> request::Outcome { + match self { + Ok(val) => Success(val), + Err(err) => Error((error, err)) + } + } + + #[inline] + fn or_forward(self, status: Status) -> request::Outcome { + match self { + Ok(val) => Success(val), + Err(_) => Forward(status) + } + } +} + +impl<'r, 'o: 'r> IntoOutcome> for response::Result<'o> { + type Error = (); + type Forward = (Data<'r>, Status); + + #[inline] + fn or_error(self, _: ()) -> route::Outcome<'r> { + match self { + Ok(val) => Success(val), + Err(status) => Error(status), + } + } + + #[inline] + fn or_forward(self, (data, forward): (Data<'r>, Status)) -> route::Outcome<'r> { + match self { + Ok(val) => Success(val), + Err(_) => Forward((data, forward)) + } } } diff --git a/core/lib/src/request/from_request.rs b/core/lib/src/request/from_request.rs index 3d048e85..203c7d49 100644 --- a/core/lib/src/request/from_request.rs +++ b/core/lib/src/request/from_request.rs @@ -3,36 +3,14 @@ use std::fmt::Debug; use std::net::{IpAddr, SocketAddr}; use crate::{Request, Route}; -use crate::outcome::{self, IntoOutcome}; -use crate::outcome::Outcome::*; +use crate::outcome::{self, Outcome::*}; -use crate::http::{Status, ContentType, Accept, Method, CookieJar}; use crate::http::uri::{Host, Origin}; +use crate::http::{Status, ContentType, Accept, Method, CookieJar}; /// Type alias for the `Outcome` of a `FromRequest` conversion. pub type Outcome = outcome::Outcome; -impl IntoOutcome for Result { - type Error = Status; - type Forward = Status; - - #[inline] - fn into_outcome(self, status: Status) -> Outcome { - match self { - Ok(val) => Success(val), - Err(err) => Error((status, err)) - } - } - - #[inline] - fn or_forward(self, status: Status) -> Outcome { - match self { - Ok(val) => Success(val), - Err(_) => Forward(status) - } - } -} - /// Trait implemented by request guards to derive a value from incoming /// requests. /// @@ -136,7 +114,8 @@ impl IntoOutcome for Result { /// * **&Host** /// /// Extracts the [`Host`] from the incoming request, if it exists. See -/// [`Request::host()`] for details. +/// [`Request::host()`] for details. If it does not exist, the request is +/// forwarded with a 500 Internal Server Error status. /// /// * **&Route** /// @@ -162,23 +141,30 @@ impl IntoOutcome for Result { /// /// _This implementation always returns successfully._ /// -/// * **ContentType** +/// * **&ContentType** /// /// Extracts the [`ContentType`] from the incoming request via /// [`Request::content_type()`]. If the request didn't specify a -/// Content-Type, the request is forwarded with a 404 Not Found status. +/// Content-Type, the request is forwarded with a 500 Internal Server Error +/// status. /// -/// * **IpAddr** +/// * **&ContentType** +/// +/// Extracts the [`Accept`] from the incoming request via +/// [`Request::accept()`]. If the request didn't specify an `Accept`, the +/// request is forwarded with a 500 Internal Server Error status. +/// +/// * ***IpAddr** /// /// Extracts the client ip address of the incoming request as an [`IpAddr`] /// via [`Request::client_ip()`]. If the client's IP address is not known, -/// the request is forwarded with a 404 Not Found status. +/// the request is forwarded with a 500 Internal Server Error status. /// /// * **SocketAddr** /// /// Extracts the remote address of the incoming request as a [`SocketAddr`] /// via [`Request::remote()`]. If the remote address is not known, the -/// request is forwarded with a 404 Not Found status. +/// request is forwarded with a 500 Internal Server Error status. /// /// * **Option<T>** _where_ **T: FromRequest** /// @@ -422,7 +408,7 @@ impl<'r> FromRequest<'r> for &'r Host<'r> { async fn from_request(request: &'r Request<'_>) -> Outcome { match request.host() { Some(host) => Success(host), - None => Forward(Status::NotFound) + None => Forward(Status::InternalServerError) } } } @@ -455,7 +441,7 @@ impl<'r> FromRequest<'r> for &'r Accept { async fn from_request(request: &'r Request<'_>) -> Outcome { match request.accept() { Some(accept) => Success(accept), - None => Forward(Status::NotFound) + None => Forward(Status::InternalServerError) } } } @@ -467,7 +453,7 @@ impl<'r> FromRequest<'r> for &'r ContentType { async fn from_request(request: &'r Request<'_>) -> Outcome { match request.content_type() { Some(content_type) => Success(content_type), - None => Forward(Status::NotFound) + None => Forward(Status::InternalServerError) } } } @@ -479,7 +465,7 @@ impl<'r> FromRequest<'r> for IpAddr { async fn from_request(request: &'r Request<'_>) -> Outcome { match request.client_ip() { Some(addr) => Success(addr), - None => Forward(Status::NotFound) + None => Forward(Status::InternalServerError) } } } @@ -491,7 +477,7 @@ impl<'r> FromRequest<'r> for SocketAddr { async fn from_request(request: &'r Request<'_>) -> Outcome { match request.remote() { Some(addr) => Success(addr), - None => Forward(Status::NotFound) + None => Forward(Status::InternalServerError) } } } diff --git a/core/lib/src/response/flash.rs b/core/lib/src/response/flash.rs index 187223ab..688e6d0e 100644 --- a/core/lib/src/response/flash.rs +++ b/core/lib/src/response/flash.rs @@ -259,7 +259,7 @@ impl<'r> FromRequest<'r> for FlashMessage<'r> { Ok(i) if i <= kv.len() => Ok(Flash::named(&kv[..i], &kv[i..], req)), _ => Err(()) } - }).into_outcome(Status::BadRequest) + }).or_error(Status::BadRequest) } } diff --git a/core/lib/src/route/handler.rs b/core/lib/src/route/handler.rs index d139942b..e29be6d5 100644 --- a/core/lib/src/route/handler.rs +++ b/core/lib/src/route/handler.rs @@ -218,31 +218,6 @@ impl<'r, 'o: 'r> Outcome<'o> { } } - /// Return the `Outcome` of response to `req` from `responder`. - /// - /// If the responder returns `Ok`, an outcome of `Success` is returned with - /// the response. If the responder returns `Err`, an outcome of `Forward` - /// with a status of `404 Not Found` is returned. - /// - /// # Example - /// - /// ```rust - /// use rocket::{Request, Data, route}; - /// - /// fn str_responder<'r>(req: &'r Request, data: Data<'r>) -> route::Outcome<'r> { - /// route::Outcome::from_or_forward(req, data, "Hello, world!") - /// } - /// ``` - #[inline] - pub fn from_or_forward(req: &'r Request<'_>, data: Data<'r>, responder: R) -> Outcome<'r> - where R: Responder<'r, 'o> - { - match responder.respond_to(req) { - Ok(response) => Outcome::Success(response), - Err(_) => Outcome::Forward((data, Status::NotFound)) - } - } - /// Return an `Outcome` of `Error` with the status code `code`. This is /// equivalent to `Outcome::Error(code)`. /// @@ -263,8 +238,8 @@ impl<'r, 'o: 'r> Outcome<'o> { Outcome::Error(code) } - /// Return an `Outcome` of `Forward` with the data `data`. This is - /// equivalent to `Outcome::Forward((data, Status::NotFound))`. + /// Return an `Outcome` of `Forward` with the data `data` and status + /// `status`. This is equivalent to `Outcome::Forward((data, status))`. /// /// This method exists to be used during manual routing. /// @@ -272,14 +247,15 @@ impl<'r, 'o: 'r> Outcome<'o> { /// /// ```rust /// use rocket::{Request, Data, route}; + /// use rocket::http::Status; /// /// fn always_forward<'r>(_: &'r Request, data: Data<'r>) -> route::Outcome<'r> { - /// route::Outcome::forward(data) + /// route::Outcome::forward(data, Status::InternalServerError) /// } /// ``` #[inline(always)] - pub fn forward(data: Data<'r>) -> Outcome<'r> { - Outcome::Forward((data, Status::NotFound)) + pub fn forward(data: Data<'r>, status: Status) -> Outcome<'r> { + Outcome::Forward((data, status)) } } diff --git a/core/lib/src/server.rs b/core/lib/src/server.rs index e874b0ff..800c9dbe 100644 --- a/core/lib/src/server.rs +++ b/core/lib/src/server.rs @@ -334,7 +334,7 @@ impl Rocket { // Check if the request processing completed (Some) or if the // request needs to be forwarded. If it does, continue the loop // (None) to try again. - info_!("{} {}", "Outcome:".primary().bold(), outcome); + info_!("{}", outcome.log_display()); match outcome { o@Outcome::Success(_) | o@Outcome::Error(_) => return o, Outcome::Forward(forwarded) => (data, status) = forwarded, diff --git a/examples/error-handling/src/tests.rs b/examples/error-handling/src/tests.rs index db5ac18d..585913e1 100644 --- a/examples/error-handling/src/tests.rs +++ b/examples/error-handling/src/tests.rs @@ -46,7 +46,15 @@ fn forced_error() { fn test_hello_invalid_age() { let client = Client::tracked(super::rocket()).unwrap(); - for path in &["Ford/-129", "Trillian/128", "foo/bar/baz"] { + for path in &["Ford/-129", "Trillian/128"] { + let request = client.get(format!("/hello/{}", path)); + let expected = super::default_catcher(Status::UnprocessableEntity, request.inner()); + let response = request.dispatch(); + assert_eq!(response.status(), Status::UnprocessableEntity); + assert_eq!(response.into_string().unwrap(), expected.1); + } + + for path in &["foo/bar/baz"] { let request = client.get(format!("/hello/{}", path)); let expected = super::hello_not_found(request.inner()); let response = request.dispatch(); @@ -59,7 +67,15 @@ fn test_hello_invalid_age() { fn test_hello_sergio() { let client = Client::tracked(super::rocket()).unwrap(); - for path in &["oops", "-129", "foo/bar", "/foo/bar/baz"] { + for path in &["oops", "-129"] { + let request = client.get(format!("/hello/Sergio/{}", path)); + let expected = super::sergio_error(); + let response = request.dispatch(); + assert_eq!(response.status(), Status::UnprocessableEntity); + assert_eq!(response.into_string().unwrap(), expected); + } + + for path in &["foo/bar", "/foo/bar/baz"] { let request = client.get(format!("/hello/Sergio/{}", path)); let expected = super::sergio_error(); let response = request.dispatch(); diff --git a/examples/hello/src/tests.rs b/examples/hello/src/tests.rs index fd5b628d..130ff0f3 100644 --- a/examples/hello/src/tests.rs +++ b/examples/hello/src/tests.rs @@ -62,10 +62,10 @@ fn wave() { let response = client.get(uri).dispatch(); assert_eq!(response.into_string().unwrap(), expected); - for bad_age in &["1000", "-1", "bird", "?"] { + for bad_age in &["1000", "-1", "bird"] { let bad_uri = format!("/wave/{}/{}", name, bad_age); let response = client.get(bad_uri).dispatch(); - assert_eq!(response.status(), Status::NotFound); + assert_eq!(response.status(), Status::UnprocessableEntity); } } } diff --git a/examples/manual-routing/src/main.rs b/examples/manual-routing/src/main.rs index 2da4c42a..e4a21620 100644 --- a/examples/manual-routing/src/main.rs +++ b/examples/manual-routing/src/main.rs @@ -9,7 +9,7 @@ use rocket::outcome::{try_outcome, IntoOutcome}; use rocket::tokio::fs::File; fn forward<'r>(_req: &'r Request, data: Data<'r>) -> route::BoxFuture<'r> { - Box::pin(async move { route::Outcome::forward(data) }) + Box::pin(async move { route::Outcome::forward(data, Status::NotFound) }) } fn hi<'r>(req: &'r Request, _: Data<'r>) -> route::BoxFuture<'r> { @@ -27,7 +27,7 @@ fn name<'r>(req: &'r Request, _: Data<'r>) -> route::BoxFuture<'r> { fn echo_url<'r>(req: &'r Request, _: Data<'r>) -> route::BoxFuture<'r> { let param_outcome = req.param::<&str>(1) .and_then(Result::ok) - .into_outcome(Status::BadRequest); + .or_error(Status::BadRequest); Box::pin(async move { route::Outcome::from(req, try_outcome!(param_outcome)) diff --git a/examples/serialization/src/tests.rs b/examples/serialization/src/tests.rs index 1a7846ba..8a46c13d 100644 --- a/examples/serialization/src/tests.rs +++ b/examples/serialization/src/tests.rs @@ -34,8 +34,7 @@ fn json_bad_get_put() { // Try to get a message with an invalid ID. let res = client.get("/json/hi").header(ContentType::JSON).dispatch(); - assert_eq!(res.status(), Status::NotFound); - assert!(res.into_string().unwrap().contains("error")); + assert_eq!(res.status(), Status::UnprocessableEntity); // Try to put a message without a proper body. let res = client.put("/json/80").header(ContentType::JSON).dispatch(); @@ -134,5 +133,5 @@ fn uuid() { } let res = client.get("/people/not-a-uuid").dispatch(); - assert_eq!(res.status(), Status::NotFound); + assert_eq!(res.status(), Status::UnprocessableEntity); }