diff --git a/examples/middleware.rs b/examples/middleware.rs new file mode 100644 index 000000000..a2ae65bc6 --- /dev/null +++ b/examples/middleware.rs @@ -0,0 +1,101 @@ +use std::future::Future; +use std::pin::Pin; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Arc; +use tide::{Middleware, Next, Request, Response, Result, StatusCode}; + +#[derive(Debug)] +struct User { + name: String, +} + +#[derive(Default)] +struct UserDatabase; +impl UserDatabase { + async fn find_user(&self) -> Option { + Some(User { + name: "nori".into(), + }) + } +} + +// This is an example of a function middleware that uses the +// application state. Because it depends on a specific request state, +// it would likely be closely tied to a specific application +fn user_loader<'a>( + mut request: Request, + next: Next<'a, UserDatabase>, +) -> Pin + Send + 'a>> { + Box::pin(async { + if let Some(user) = request.state().find_user().await { + tide::log::trace!("user loaded", {user: user.name}); + request.set_ext(user); + next.run(request).await + // this middleware only needs to run before the endpoint, so + // it just passes through the result of Next + } else { + // do not run endpoints, we could not find a user + Ok(Response::new(StatusCode::Unauthorized)) + } + }) +} + +// +// +// this is an example of middleware that keeps its own state and could +// be provided as a third party crate +#[derive(Default)] +struct RequestCounterMiddleware { + requests_counted: Arc, +} + +impl RequestCounterMiddleware { + fn new(start: usize) -> Self { + Self { + requests_counted: Arc::new(AtomicUsize::new(start)), + } + } +} + +struct RequestCount(usize); + +impl Middleware for RequestCounterMiddleware { + fn handle<'a>( + &'a self, + mut req: Request, + next: Next<'a, State>, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + let count = self.requests_counted.fetch_add(1, Ordering::Relaxed); + tide::log::trace!("request counter", { count: count }); + req.set_ext(RequestCount(count)); + + let mut response = next.run(req).await?; + + response = response.set_header("request-number", count.to_string()); + Ok(response) + }) + } +} + +#[async_std::main] +async fn main() -> Result<()> { + tide::log::start(); + let mut app = tide::with_state(UserDatabase::default()); + + app.middleware(user_loader); + app.middleware(RequestCounterMiddleware::new(0)); + + app.at("/").get(|req: Request<_>| async move { + let count: &RequestCount = req.ext().unwrap(); + let user: &User = req.ext().unwrap(); + + Ok(format!( + "Hello {}, this was request number {}!", + user.name, count.0 + )) + }); + + app.listen("127.0.0.1:8080").await?; + Ok(()) +} diff --git a/src/middleware.rs b/src/middleware.rs index 19424f01f..573f005ea 100644 --- a/src/middleware.rs +++ b/src/middleware.rs @@ -17,9 +17,14 @@ pub trait Middleware: 'static + Send + Sync { /// Asynchronously handle the request, and return a response. fn handle<'a>( &'a self, - cx: Request, + request: Request, next: Next<'a, State>, ) -> BoxFuture<'a, crate::Result>; + + /// Set the middleware's name. By default it uses the type signature. + fn name(&self) -> &str { + std::any::type_name::() + } } impl Middleware for F diff --git a/src/server.rs b/src/server.rs index 029aadab3..d0bff9d96 100644 --- a/src/server.rs +++ b/src/server.rs @@ -6,8 +6,6 @@ use async_std::prelude::*; use async_std::sync::Arc; use async_std::task; -use std::fmt::Debug; - use crate::cookies; use crate::log; use crate::middleware::{Middleware, Next}; @@ -268,9 +266,9 @@ impl Server { /// order in which it is applied. pub fn middleware(&mut self, middleware: M) -> &mut Self where - M: Middleware + Debug, + M: Middleware, { - log::trace!("Adding middleware {:?}", middleware); + log::trace!("Adding middleware {}", middleware.name()); let m = Arc::get_mut(&mut self.middleware) .expect("Registering middleware is not possible after the Server has started"); m.push(Arc::new(middleware));