Request Guards

Request guards are one of Rocket’s most powerful instruments. As the name might imply, a request guard protects a handler from being called erroneously based on information contained in an incoming request. More specifically, a request guard is a type that represents an arbitrary validation policy. The validation policy is implemented through the FromRequest trait. Every type that implements FromRequest is a request guard.

Request guards appear as inputs to handlers. An arbitrary number of request guards can appear as arguments in a route handler. Rocket will automatically invoke the FromRequest implementation for request guards before calling the handler. Rocket only dispatches requests to a handler when all of its guards pass.

As an example, the following dummy handler makes use of three request guards, A, B, and C. An input can be identified as a request guard if it is not named in the route attribute. This is why param is not a request guard.

  1. #[get("/<param>")]
  2. fn index(param: isize, a: A, b: B, c: C) -> ... { ... }

Request guards always fire in left-to-right declaration order. In the example above, the order will be A followed by B followed by C. Failure is short-circuiting; if one guard fails, the remaining are not attempted. To learn more about request guards and implementing them, see the FromRequest documentation.

Custom Guards

You can implement FromRequest for your own types. For instance, to protect a sensitive route from running unless an ApiKey is present in the request headers, you might create an ApiKey type that implements FromRequest and then use it as a request guard:

  1. #[get("/sensitive")]
  2. fn sensitive(key: ApiKey) -> &'static str { ... }

You might also implement FromRequest for an AdminUser type that authenticates an administrator using incoming cookies. Then, any handler with an AdminUser or ApiKey type in its argument list is assured to only be invoked if the appropriate conditions are met. Request guards centralize policies, resulting in a simpler, safer, and more secure applications.

Forwarding Guards

Request guards and forwarding are a powerful combination for enforcing policies. To illustrate, we consider how a simple authorization system might be implemented using these mechanisms.

We start with two request guards:

  • User: A regular, authenticated user.

    The FromRequest implementation for User checks that a cookie identifies a user and returns a User value if so. If no user can be authenticated, the guard forwards.

  • AdminUser: A user authenticated as an administrator.

    The FromRequest implementation for AdminUser checks that a cookie identifies an administrative user and returns an AdminUser value if so. If no user can be authenticated, the guard forwards.

We now use these two guards in combination with forwarding to implement the following three routes, each leading to an administrative control panel at /admin:

  1. #[get("/admin")]
  2. fn admin_panel(admin: AdminUser) -> &'static str {
  3. "Hello, administrator. This is the admin panel!"
  4. }
  5. #[get("/admin", rank = 2)]
  6. fn admin_panel_user(user: User) -> &'static str {
  7. "Sorry, you must be an administrator to access this page."
  8. }
  9. #[get("/admin", rank = 3)]
  10. fn admin_panel_redirect() -> Redirect {
  11. Redirect::to("/login")
  12. }

The three routes above encode authentication and authorization. The admin_panel route only succeeds if an administrator is logged in. Only then is the admin panel displayed. If the user is not an admin, the AdminUser route will forward. Since the admin_panel_user route is ranked next highest, it is attempted next. This route succeeds if there is any user signed in, and an authorization failure message is displayed. Finally, if a user isn’t signed in, the admin_panel_redirect route is attempted. Since this route has no guards, it always succeeds. The user is redirected to a log in page.