Skip to content

Middleware

Middleware runs before a request reaches a controller and has the ability to modify the request, or block it from reaching the controller entirely. Middleware is used to validate incoming requests against some conditions, e.g. required headers. It can also be used to mark requests with special tags, by adding custom headers.

Using middleware

Middleware needs to be specified on each controller. By default, all controllers come with no middleware, so requests processed by them are unmodified from their original state.

Define middleware

Middleware, similar to controllers, is any struct which implements the Middleware trait. The only method that needs implementation is the async fn handle_request method, which accepts a Request and must return an Outcome.

If the request is allowed to proceed, Outcome::Forward is returned, containing the request, in its modified or unchanged form. If on the other hand, the request failed some kind of validation, Outcome::Stop must be returned with a Response, for example:

use rwf::controller::middleware::prelude::*;

struct RequiredHeaders {
    headers: Vec<String>,
}

impl Default for RequiredHeaders {
    fn default() -> Self {
        Self {
            headers: vec![
                "X-Request-Id".to_string()
            ],
        }
    }
}

#[async_trait]
impl Middleware for RequiredHeaders {
    async fn handle_request(&self, request: Request) -> Result<Outcome, Error> {
        for header in &self.headers {
            let header = request.headers().get(header);

            if header.is_none() {
                return Ok(Outcome::Stop(request, Response::bad_request()));
            }
        }

        Ok(Outcome::Forward(request))
    }
}

Enable middleware

Enabling middleware needs to be done at the controller level. For each controller where you want the middleware to run, add it to the struct fields and instantiate it when the controller is created:

struct Index {
    middleware: MiddlewareSet,
}

impl Default for Index {
    fn default() -> Self {
        Index {
            middleware: MiddlewareSet::new(vec![
                RequiredHeaders::default()
                    .middleware(),
            ])
        }
    }
}

When implementing the Controller trait for your controller, implement the middleware method as well:

#[async_trait]
impl Controller for Index {
    // This controller has middleware.
    fn middleware(&self) -> &MiddlewareSet {
        &self.middleware
    }

    // Middleware will run before this method.
    async fn handle(&self, request: &Request) -> Result<Response, Error> {
        /* ... */
    }
}

Adding a controller with middleware to the server requires no special code, since middleware is handled by the Controller trait internally.