mirror of https://github.com/rwf2/Rocket.git
Implement dynamic request handling via 'Handler' trait.
This commit is contained in:
parent
9bf585496c
commit
29c9cffdbe
|
@ -1,12 +1,15 @@
|
||||||
use handler::{Handler, ErrorHandler};
|
use {Request, Data};
|
||||||
|
use handler::{Outcome, ErrorHandler};
|
||||||
use http::{Method, MediaType};
|
use http::{Method, MediaType};
|
||||||
|
|
||||||
|
pub type StaticHandler = for<'r> fn(&'r Request, Data) -> Outcome<'r>;
|
||||||
|
|
||||||
pub struct StaticRouteInfo {
|
pub struct StaticRouteInfo {
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
pub method: Method,
|
pub method: Method,
|
||||||
pub path: &'static str,
|
pub path: &'static str,
|
||||||
pub format: Option<MediaType>,
|
pub format: Option<MediaType>,
|
||||||
pub handler: Handler,
|
pub handler: StaticHandler,
|
||||||
pub rank: Option<isize>,
|
pub rank: Option<isize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
//! The types of request and error handlers and their return values.
|
//! Types and traits for request and error handlers and their return values.
|
||||||
|
|
||||||
use data::Data;
|
use data::Data;
|
||||||
use request::Request;
|
use request::Request;
|
||||||
|
@ -10,8 +10,179 @@ use outcome;
|
||||||
/// Type alias for the `Outcome` of a `Handler`.
|
/// Type alias for the `Outcome` of a `Handler`.
|
||||||
pub type Outcome<'r> = outcome::Outcome<Response<'r>, Status, Data>;
|
pub type Outcome<'r> = outcome::Outcome<Response<'r>, Status, Data>;
|
||||||
|
|
||||||
/// The type of a request handler.
|
/// Trait implemented by types that can handle requests.
|
||||||
pub type Handler = for<'r> fn(&'r Request, Data) -> Outcome<'r>;
|
///
|
||||||
|
/// In general, you will never need to implement `Handler` manually or be
|
||||||
|
/// concerned about the `Handler` trait; Rocket's code generation handles
|
||||||
|
/// everything for you. You only need to learn about this trait if you want to
|
||||||
|
/// provide an external, library-based mechanism to handle requests where
|
||||||
|
/// request handling depends on input from the user. In other words, if you want
|
||||||
|
/// to write a plugin for Rocket that looks mostly like a static route but need
|
||||||
|
/// user provided state to make a request handling decision, you should consider
|
||||||
|
/// implementing a custom `Handler`.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// Say you'd like to write a handler that changes its functionality based on an
|
||||||
|
/// enum value that the user provides:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// #[derive(Copy, Clone)]
|
||||||
|
/// enum Kind {
|
||||||
|
/// Simple,
|
||||||
|
/// Intermediate,
|
||||||
|
/// Complex,
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Such a handler might be written and used as follows:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # #[derive(Copy, Clone)]
|
||||||
|
/// # enum Kind {
|
||||||
|
/// # Simple,
|
||||||
|
/// # Intermediate,
|
||||||
|
/// # Complex,
|
||||||
|
/// # }
|
||||||
|
/// use rocket::{Request, Data, Route, http::Method};
|
||||||
|
/// use rocket::handler::{self, Handler, Outcome};
|
||||||
|
///
|
||||||
|
/// #[derive(Clone)]
|
||||||
|
/// struct CustomHandler(Kind);
|
||||||
|
///
|
||||||
|
/// impl CustomHandler {
|
||||||
|
/// pub fn new(kind: Kind) -> Vec<Route> {
|
||||||
|
/// vec![Route::new(Method::Get, "/", CustomHandler(kind))]
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl Handler for CustomHandler {
|
||||||
|
/// fn handle<'r>(&self, req: &'r Request, data: Data) -> Outcome<'r> {
|
||||||
|
/// match self.0 {
|
||||||
|
/// Kind::Simple => Outcome::from(req, "simple"),
|
||||||
|
/// Kind::Intermediate => Outcome::from(req, "intermediate"),
|
||||||
|
/// Kind::Complex => Outcome::from(req, "complex"),
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// # if false {
|
||||||
|
/// rocket::ignite()
|
||||||
|
/// .mount("/", CustomHandler::new(Kind::Simple))
|
||||||
|
/// .launch();
|
||||||
|
/// # }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Note the following:
|
||||||
|
///
|
||||||
|
/// 1. `CustomHandler` implements `Clone`. This is required so that
|
||||||
|
/// `CustomHandler` implements `Cloneable` automatically. The `Cloneable`
|
||||||
|
/// trait serves no other purpose but to ensure that every `Handler` can be
|
||||||
|
/// cloned, allowing `Route`s to be cloned.
|
||||||
|
/// 2. The `CustomHandler::new()` method returns a vector of routes so that
|
||||||
|
/// the user can trivially mount the handler.
|
||||||
|
/// 3. Unlike static-function-based handlers, this custom handler can make use
|
||||||
|
/// of any internal state.
|
||||||
|
///
|
||||||
|
/// # Alternatives
|
||||||
|
///
|
||||||
|
/// The previous example could have been implemented using a combination of
|
||||||
|
/// managed state and a static route, as follows:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # #![feature(plugin, decl_macro)]
|
||||||
|
/// # #![plugin(rocket_codegen)]
|
||||||
|
/// # extern crate rocket;
|
||||||
|
/// #
|
||||||
|
/// # #[derive(Copy, Clone)]
|
||||||
|
/// # enum Kind {
|
||||||
|
/// # Simple,
|
||||||
|
/// # Intermediate,
|
||||||
|
/// # Complex,
|
||||||
|
/// # }
|
||||||
|
/// #
|
||||||
|
/// use rocket::State;
|
||||||
|
///
|
||||||
|
/// #[get("/")]
|
||||||
|
/// fn custom_handler(state: State<Kind>) -> &'static str {
|
||||||
|
/// match *state {
|
||||||
|
/// Kind::Simple => "simple",
|
||||||
|
/// Kind::Intermediate => "intermediate",
|
||||||
|
/// Kind::Complex => "complex",
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// # if false {
|
||||||
|
/// rocket::ignite()
|
||||||
|
/// .mount("/", routes![custom_handler])
|
||||||
|
/// .manage(Kind::Simple)
|
||||||
|
/// .launch();
|
||||||
|
/// # }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Pros:
|
||||||
|
///
|
||||||
|
/// * The handler is easier to implement since Rocket's code generation
|
||||||
|
/// ensures type-safety at all levels.
|
||||||
|
///
|
||||||
|
/// Cons:
|
||||||
|
///
|
||||||
|
/// * Only one `Kind` can be stored in managed state. As such, only one
|
||||||
|
/// variant of the custom handler can be used.
|
||||||
|
/// * The user must remember to manually call `rocket.manage(state)`.
|
||||||
|
///
|
||||||
|
/// Use this alternative when a single configuration is desired and your custom
|
||||||
|
/// handler is private to your application. For all other cases, a custom
|
||||||
|
/// `Handler` implementation is preferred.
|
||||||
|
pub trait Handler: Cloneable + Send + Sync + 'static {
|
||||||
|
/// Called by Rocket when a `Request` with its associated `Data` should be
|
||||||
|
/// handled by this handler.
|
||||||
|
///
|
||||||
|
/// The variant of `Outcome` returned determines what Rocket does next. If
|
||||||
|
/// the return value is a `Success(Response)`, the wrapped `Response` is
|
||||||
|
/// used to respond to the client. If the return value is a
|
||||||
|
/// `Failure(Status)`, the error catcher for `Status` is invoked to generate
|
||||||
|
/// a response. Otherwise, if the return value is `Forward(Data)`, the next
|
||||||
|
/// matching route is attempted. If there are no other matching routes, the
|
||||||
|
/// `404` error catcher is invoked.
|
||||||
|
fn handle<'r>(&self, request: &'r Request, data: Data) -> Outcome<'r>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unfortunate but necessary hack to be able to clone a `Box<Handler>`.
|
||||||
|
///
|
||||||
|
/// This trait should _never_ (and cannot, due to coherence) be implemented by
|
||||||
|
/// any type. Instead, implement `Clone`. All types that implement `Clone` and
|
||||||
|
/// `Handler` automatically implement `Cloneable`.
|
||||||
|
pub trait Cloneable {
|
||||||
|
fn clone_handler(&self) -> Box<Handler>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Handler + Clone> Cloneable for T {
|
||||||
|
#[inline(always)]
|
||||||
|
fn clone_handler(&self) -> Box<Handler> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Box<Handler> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn clone(&self) -> Box<Handler> {
|
||||||
|
self.clone_handler()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Clone + Sync + Send + 'static> Handler for F
|
||||||
|
where for<'r> F: Fn(&'r Request, Data) -> Outcome<'r>
|
||||||
|
{
|
||||||
|
#[inline(always)]
|
||||||
|
fn handle<'r>(&self, req: &'r Request, data: Data) -> Outcome<'r> {
|
||||||
|
self(req, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The type of an error handler.
|
/// The type of an error handler.
|
||||||
pub type ErrorHandler = for<'r> fn(Error, &'r Request) -> response::Result<'r>;
|
pub type ErrorHandler = for<'r> fn(Error, &'r Request) -> response::Result<'r>;
|
||||||
|
|
|
@ -63,16 +63,12 @@ impl Client {
|
||||||
/// is created for cookie tracking. Otherwise, the internal `CookieJar` is
|
/// is created for cookie tracking. Otherwise, the internal `CookieJar` is
|
||||||
/// set to `None`.
|
/// set to `None`.
|
||||||
fn _new(rocket: Rocket, tracked: bool) -> Result<Client, LaunchError> {
|
fn _new(rocket: Rocket, tracked: bool) -> Result<Client, LaunchError> {
|
||||||
if let Some(err) = rocket.prelaunch_check() {
|
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
let cookies = match tracked {
|
let cookies = match tracked {
|
||||||
true => Some(RefCell::new(CookieJar::new())),
|
true => Some(RefCell::new(CookieJar::new())),
|
||||||
false => None
|
false => None
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Client { rocket, cookies })
|
Ok(Client { rocket: rocket.prelaunch_check()?, cookies })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a new `Client` from an instance of `Rocket` with cookie
|
/// Construct a new `Client` from an instance of `Rocket` with cookie
|
||||||
|
|
|
@ -286,7 +286,7 @@ impl Rocket {
|
||||||
request.set_route(route);
|
request.set_route(route);
|
||||||
|
|
||||||
// Dispatch the request to the handler.
|
// Dispatch the request to the handler.
|
||||||
let outcome = (route.handler)(request, data);
|
let outcome = route.handler.handle(request, data);
|
||||||
|
|
||||||
// Check if the request processing completed or if the request needs
|
// Check if the request processing completed or if the request needs
|
||||||
// to be forwarded. If it does, continue the loop to try again.
|
// to be forwarded. If it does, continue the loop to try again.
|
||||||
|
@ -482,7 +482,7 @@ impl Rocket {
|
||||||
/// use rocket::handler::Outcome;
|
/// use rocket::handler::Outcome;
|
||||||
/// use rocket::http::Method::*;
|
/// use rocket::http::Method::*;
|
||||||
///
|
///
|
||||||
/// fn hi(req: &Request, _: Data) -> Outcome<'static> {
|
/// fn hi<'r>(req: &'r Request, _: Data) -> Outcome<'r> {
|
||||||
/// Outcome::from(req, "Hello!")
|
/// Outcome::from(req, "Hello!")
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
|
@ -666,16 +666,17 @@ impl Rocket {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
crate fn prelaunch_check(&self) -> Option<LaunchError> {
|
crate fn prelaunch_check(mut self) -> Result<Rocket, LaunchError> {
|
||||||
let collisions = self.router.collisions();
|
self.router = match self.router.collisions() {
|
||||||
if !collisions.is_empty() {
|
Ok(router) => router,
|
||||||
let owned = collisions.iter().map(|&(a, b)| (a.clone(), b.clone()));
|
Err(e) => return Err(LaunchError::new(LaunchErrorKind::Collision(e)))
|
||||||
Some(LaunchError::new(LaunchErrorKind::Collision(owned.collect())))
|
};
|
||||||
} else if let Some(failures) = self.fairings.failures() {
|
|
||||||
Some(LaunchError::new(LaunchErrorKind::FailedFairings(failures.to_vec())))
|
if let Some(failures) = self.fairings.failures() {
|
||||||
} else {
|
return Err(LaunchError::new(LaunchErrorKind::FailedFairings(failures.to_vec())))
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Starts the application server and begins listening for and dispatching
|
/// Starts the application server and begins listening for and dispatching
|
||||||
|
@ -699,9 +700,10 @@ impl Rocket {
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn launch(mut self) -> LaunchError {
|
pub fn launch(mut self) -> LaunchError {
|
||||||
if let Some(error) = self.prelaunch_check() {
|
self = match self.prelaunch_check() {
|
||||||
return error;
|
Ok(rocket) => rocket,
|
||||||
}
|
Err(launch_error) => return launch_error
|
||||||
|
};
|
||||||
|
|
||||||
self.fairings.pretty_print_counts();
|
self.fairings.pretty_print_counts();
|
||||||
|
|
||||||
|
|
|
@ -119,19 +119,13 @@ mod tests {
|
||||||
use rocket::Rocket;
|
use rocket::Rocket;
|
||||||
use config::Config;
|
use config::Config;
|
||||||
use request::Request;
|
use request::Request;
|
||||||
use data::Data;
|
use router::{dummy_handler, route::Route};
|
||||||
use handler::Outcome;
|
|
||||||
use router::route::Route;
|
|
||||||
use http::{Method, MediaType, ContentType, Accept};
|
use http::{Method, MediaType, ContentType, Accept};
|
||||||
use http::uri::Origin;
|
use http::uri::Origin;
|
||||||
use http::Method::*;
|
use http::Method::*;
|
||||||
|
|
||||||
type SimpleRoute = (Method, &'static str);
|
type SimpleRoute = (Method, &'static str);
|
||||||
|
|
||||||
fn dummy_handler(req: &Request, _: Data) -> Outcome<'static> {
|
|
||||||
Outcome::from(req, "hi")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn m_collide(a: SimpleRoute, b: SimpleRoute) -> bool {
|
fn m_collide(a: SimpleRoute, b: SimpleRoute) -> bool {
|
||||||
let route_a = Route::new(a.0, a.1.to_string(), dummy_handler);
|
let route_a = Route::new(a.0, a.1.to_string(), dummy_handler);
|
||||||
route_a.collides_with(&Route::new(b.0, b.1.to_string(), dummy_handler))
|
route_a.collides_with(&Route::new(b.0, b.1.to_string(), dummy_handler))
|
||||||
|
|
|
@ -3,8 +3,8 @@ mod route;
|
||||||
|
|
||||||
use std::collections::hash_map::HashMap;
|
use std::collections::hash_map::HashMap;
|
||||||
|
|
||||||
use self::collider::Collider;
|
|
||||||
pub use self::route::Route;
|
pub use self::route::Route;
|
||||||
|
use self::collider::Collider;
|
||||||
|
|
||||||
use request::Request;
|
use request::Request;
|
||||||
use http::Method;
|
use http::Method;
|
||||||
|
@ -12,6 +12,11 @@ use http::Method;
|
||||||
// type Selector = (Method, usize);
|
// type Selector = (Method, usize);
|
||||||
type Selector = Method;
|
type Selector = Method;
|
||||||
|
|
||||||
|
// A handler to use when one is needed temporarily.
|
||||||
|
crate fn dummy_handler<'r>(r: &'r ::Request, _: ::Data) -> ::handler::Outcome<'r> {
|
||||||
|
::Outcome::from(r, ())
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Router {
|
pub struct Router {
|
||||||
routes: HashMap<Selector, Vec<Route>>, // using 'selector' for now
|
routes: HashMap<Selector, Vec<Route>>, // using 'selector' for now
|
||||||
|
@ -42,36 +47,57 @@ impl Router {
|
||||||
matches
|
matches
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn collisions(&self) -> Vec<(&Route, &Route)> {
|
crate fn collisions(mut self) -> Result<Router, Vec<(Route, Route)>> {
|
||||||
let mut result = vec![];
|
let mut collisions = vec![];
|
||||||
for routes in self.routes.values() {
|
for routes in self.routes.values_mut() {
|
||||||
for (i, a_route) in routes.iter().enumerate() {
|
for i in 0..routes.len() {
|
||||||
for b_route in routes.iter().skip(i + 1) {
|
let (left, right) = routes.split_at_mut(i);
|
||||||
if a_route.collides_with(b_route) {
|
for a_route in left.iter_mut() {
|
||||||
result.push((a_route, b_route));
|
for b_route in right.iter_mut() {
|
||||||
|
if a_route.collides_with(b_route) {
|
||||||
|
let dummy_a = Route::new(Method::Get, "/", dummy_handler);
|
||||||
|
let a = ::std::mem::replace(a_route, dummy_a);
|
||||||
|
let dummy_b = Route::new(Method::Get, "/", dummy_handler);
|
||||||
|
let b = ::std::mem::replace(b_route, dummy_b);
|
||||||
|
collisions.push((a, b));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
if collisions.is_empty() {
|
||||||
}
|
Ok(self)
|
||||||
|
} else {
|
||||||
// This is slow. Don't expose this publicly; only for tests.
|
Err(collisions)
|
||||||
#[cfg(test)]
|
}
|
||||||
fn has_collisions(&self) -> bool {
|
|
||||||
!self.collisions().is_empty()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn routes<'a>(&'a self) -> impl Iterator<Item=&'a Route> + 'a {
|
pub fn routes<'a>(&'a self) -> impl Iterator<Item=&'a Route> + 'a {
|
||||||
self.routes.values().flat_map(|v| v.iter())
|
self.routes.values().flat_map(|v| v.iter())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is slow. Don't expose this publicly; only for tests.
|
||||||
|
#[cfg(test)]
|
||||||
|
fn has_collisions(&self) -> bool {
|
||||||
|
for routes in self.routes.values() {
|
||||||
|
for (i, a_route) in routes.iter().enumerate() {
|
||||||
|
for b_route in routes.iter().skip(i + 1) {
|
||||||
|
if a_route.collides_with(b_route) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::{Router, Route};
|
use super::{Router, Route, dummy_handler};
|
||||||
|
|
||||||
use rocket::Rocket;
|
use rocket::Rocket;
|
||||||
use config::Config;
|
use config::Config;
|
||||||
|
@ -79,12 +105,6 @@ mod test {
|
||||||
use http::Method::*;
|
use http::Method::*;
|
||||||
use http::uri::Origin;
|
use http::uri::Origin;
|
||||||
use request::Request;
|
use request::Request;
|
||||||
use data::Data;
|
|
||||||
use handler::Outcome;
|
|
||||||
|
|
||||||
fn dummy_handler(req: &Request, _: Data) -> Outcome<'static> {
|
|
||||||
Outcome::from(req, "hi")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn router_with_routes(routes: &[&'static str]) -> Router {
|
fn router_with_routes(routes: &[&'static str]) -> Router {
|
||||||
let mut router = Router::new();
|
let mut router = Router::new();
|
||||||
|
|
|
@ -17,7 +17,7 @@ pub struct Route {
|
||||||
/// The method this route matches against.
|
/// The method this route matches against.
|
||||||
pub method: Method,
|
pub method: Method,
|
||||||
/// The function that should be called when the route matches.
|
/// The function that should be called when the route matches.
|
||||||
pub handler: Handler,
|
pub handler: Box<Handler>,
|
||||||
/// The base mount point of this `Route`.
|
/// The base mount point of this `Route`.
|
||||||
pub base: Origin<'static>,
|
pub base: Origin<'static>,
|
||||||
/// The uri (in Rocket's route format) that should be matched against. This
|
/// The uri (in Rocket's route format) that should be matched against. This
|
||||||
|
@ -83,8 +83,8 @@ impl Route {
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if `path` is not a valid origin URI.
|
/// Panics if `path` is not a valid origin URI.
|
||||||
pub fn new<S>(method: Method, path: S, handler: Handler) -> Route
|
pub fn new<S, H>(method: Method, path: S, handler: H) -> Route
|
||||||
where S: AsRef<str>
|
where S: AsRef<str>, H: Handler + 'static
|
||||||
{
|
{
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
let origin = Origin::parse_route(path)
|
let origin = Origin::parse_route(path)
|
||||||
|
@ -115,8 +115,8 @@ impl Route {
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if `path` is not a valid origin URI.
|
/// Panics if `path` is not a valid origin URI.
|
||||||
pub fn ranked<S>(rank: isize, method: Method, path: S, handler: Handler) -> Route
|
pub fn ranked<S, H>(rank: isize, method: Method, path: S, handler: H) -> Route
|
||||||
where S: AsRef<str>
|
where S: AsRef<str>, H: Handler + 'static
|
||||||
{
|
{
|
||||||
let uri = Origin::parse_route(path.as_ref())
|
let uri = Origin::parse_route(path.as_ref())
|
||||||
.expect("invalid URI used as route path in `Route::ranked()`")
|
.expect("invalid URI used as route path in `Route::ranked()`")
|
||||||
|
@ -126,7 +126,8 @@ impl Route {
|
||||||
name: None,
|
name: None,
|
||||||
format: None,
|
format: None,
|
||||||
base: Origin::dummy(),
|
base: Origin::dummy(),
|
||||||
method, handler, rank, uri
|
handler: Box::new(handler),
|
||||||
|
method, rank, uri
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,18 +6,19 @@ mod tests;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
|
||||||
use rocket::{Request, Route, Data, Catcher, Error};
|
use rocket::{Request, Handler, Route, Data, Catcher, Error};
|
||||||
use rocket::http::{Status, RawStr};
|
use rocket::http::{Status, RawStr};
|
||||||
use rocket::response::{self, Responder};
|
use rocket::response::{self, Responder};
|
||||||
use rocket::response::status::Custom;
|
use rocket::response::status::Custom;
|
||||||
use rocket::handler::Outcome;
|
use rocket::handler::Outcome;
|
||||||
|
use rocket::outcome::IntoOutcome;
|
||||||
use rocket::http::Method::*;
|
use rocket::http::Method::*;
|
||||||
|
|
||||||
fn forward(_req: &Request, data: Data) -> Outcome<'static> {
|
fn forward<'r>(_req: &'r Request, data: Data) -> Outcome<'r> {
|
||||||
Outcome::forward(data)
|
Outcome::forward(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hi(req: &Request, _: Data) -> Outcome<'static> {
|
fn hi<'r>(req: &'r Request, _: Data) -> Outcome<'r> {
|
||||||
Outcome::from(req, "Hello!")
|
Outcome::from(req, "Hello!")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +27,7 @@ fn name<'a>(req: &'a Request, _: Data) -> Outcome<'a> {
|
||||||
Outcome::from(req, param.map(|r| r.as_str()).unwrap_or("unnamed"))
|
Outcome::from(req, param.map(|r| r.as_str()).unwrap_or("unnamed"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn echo_url(req: &Request, _: Data) -> Outcome<'static> {
|
fn echo_url<'r>(req: &'r Request, _: Data) -> Outcome<'r> {
|
||||||
let param = req.uri()
|
let param = req.uri()
|
||||||
.path()
|
.path()
|
||||||
.split_at(6)
|
.split_at(6)
|
||||||
|
@ -55,7 +56,7 @@ fn upload<'r>(req: &'r Request, data: Data) -> Outcome<'r> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_upload(req: &Request, _: Data) -> Outcome<'static> {
|
fn get_upload<'r>(req: &'r Request, _: Data) -> Outcome<'r> {
|
||||||
Outcome::from(req, File::open("/tmp/upload.txt").ok())
|
Outcome::from(req, File::open("/tmp/upload.txt").ok())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,6 +65,24 @@ fn not_found_handler<'r>(_: Error, req: &'r Request) -> response::Result<'r> {
|
||||||
res.respond_to(req)
|
res.respond_to(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct CustomHandler {
|
||||||
|
data: &'static str
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CustomHandler {
|
||||||
|
fn new(data: &'static str) -> Vec<Route> {
|
||||||
|
vec![Route::new(Get, "/<id>", Self { data })]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler for CustomHandler {
|
||||||
|
fn handle<'r>(&self, req: &'r Request, data: Data) -> Outcome<'r> {
|
||||||
|
let id = req.get_param::<&RawStr>(0).ok().or_forward(data)?;
|
||||||
|
Outcome::from(req, format!("{} - {}", self.data, id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn rocket() -> rocket::Rocket {
|
fn rocket() -> rocket::Rocket {
|
||||||
let always_forward = Route::ranked(1, Get, "/", forward);
|
let always_forward = Route::ranked(1, Get, "/", forward);
|
||||||
let hello = Route::ranked(2, Get, "/", hi);
|
let hello = Route::ranked(2, Get, "/", hi);
|
||||||
|
@ -80,6 +99,7 @@ fn rocket() -> rocket::Rocket {
|
||||||
.mount("/upload", vec![get_upload, post_upload])
|
.mount("/upload", vec![get_upload, post_upload])
|
||||||
.mount("/hello", vec![name.clone()])
|
.mount("/hello", vec![name.clone()])
|
||||||
.mount("/hi", vec![name])
|
.mount("/hi", vec![name])
|
||||||
|
.mount("/custom", CustomHandler::new("some data here"))
|
||||||
.catch(vec![not_found_catcher])
|
.catch(vec![not_found_catcher])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue