axum_extra/handler/
mod.rs

1//! Additional handler utilities.
2
3use axum::body::Body;
4use axum::extract::Request;
5use axum::{
6    extract::FromRequest,
7    handler::Handler,
8    response::{IntoResponse, Response},
9};
10use futures_util::future::{BoxFuture, FutureExt, Map};
11use std::{future::Future, marker::PhantomData};
12
13mod or;
14
15pub use self::or::Or;
16
17/// Trait for async functions that can be used to handle requests.
18///
19/// This trait is similar to [`Handler`] but rather than taking the request it takes the extracted
20/// inputs.
21///
22/// The drawbacks of this trait is that you cannot apply middleware to individual handlers like you
23/// can with [`Handler::layer`].
24pub trait HandlerCallWithExtractors<T, S>: Sized {
25    /// The type of future calling this handler returns.
26    type Future: Future<Output = Response> + Send + 'static;
27
28    /// Call the handler with the extracted inputs.
29    fn call(self, extractors: T, state: S) -> <Self as HandlerCallWithExtractors<T, S>>::Future;
30
31    /// Convert this `HandlerCallWithExtractors` into [`Handler`].
32    fn into_handler(self) -> IntoHandler<Self, T, S> {
33        IntoHandler {
34            handler: self,
35            _marker: PhantomData,
36        }
37    }
38
39    /// Chain two handlers together, running the second one if the first one rejects.
40    ///
41    /// Note that this only moves to the next handler if an extractor fails. The response from
42    /// handlers are not considered.
43    ///
44    /// # Example
45    ///
46    /// ```
47    /// use axum_extra::handler::HandlerCallWithExtractors;
48    /// use axum::{
49    ///     Router,
50    ///     async_trait,
51    ///     routing::get,
52    ///     extract::FromRequestParts,
53    /// };
54    ///
55    /// // handlers for varying levels of access
56    /// async fn admin(admin: AdminPermissions) {
57    ///     // request came from an admin
58    /// }
59    ///
60    /// async fn user(user: User) {
61    ///     // we have a `User`
62    /// }
63    ///
64    /// async fn guest() {
65    ///     // `AdminPermissions` and `User` failed, so we're just a guest
66    /// }
67    ///
68    /// // extractors for checking permissions
69    /// struct AdminPermissions {}
70    ///
71    /// #[async_trait]
72    /// impl<S> FromRequestParts<S> for AdminPermissions
73    /// where
74    ///     S: Send + Sync,
75    /// {
76    ///     // check for admin permissions...
77    ///     # type Rejection = ();
78    ///     # async fn from_request_parts(parts: &mut http::request::Parts, state: &S) -> Result<Self, Self::Rejection> {
79    ///     #     todo!()
80    ///     # }
81    /// }
82    ///
83    /// struct User {}
84    ///
85    /// #[async_trait]
86    /// impl<S> FromRequestParts<S> for User
87    /// where
88    ///     S: Send + Sync,
89    /// {
90    ///     // check for a logged in user...
91    ///     # type Rejection = ();
92    ///     # async fn from_request_parts(parts: &mut http::request::Parts, state: &S) -> Result<Self, Self::Rejection> {
93    ///     #     todo!()
94    ///     # }
95    /// }
96    ///
97    /// let app = Router::new().route(
98    ///     "/users/:id",
99    ///     get(
100    ///         // first try `admin`, if that rejects run `user`, finally falling back
101    ///         // to `guest`
102    ///         admin.or(user).or(guest)
103    ///     )
104    /// );
105    /// # let _: Router = app;
106    /// ```
107    fn or<R, Rt>(self, rhs: R) -> Or<Self, R, T, Rt, S>
108    where
109        R: HandlerCallWithExtractors<Rt, S>,
110    {
111        Or {
112            lhs: self,
113            rhs,
114            _marker: PhantomData,
115        }
116    }
117}
118
119macro_rules! impl_handler_call_with {
120     ( $($ty:ident),* $(,)? ) => {
121         #[allow(non_snake_case)]
122         impl<F, Fut, S, $($ty,)*> HandlerCallWithExtractors<($($ty,)*), S> for F
123         where
124             F: FnOnce($($ty,)*) -> Fut,
125             Fut: Future + Send + 'static,
126             Fut::Output: IntoResponse,
127         {
128             // this puts `futures_util` in our public API but thats fine in axum-extra
129             type Future = Map<Fut, fn(Fut::Output) -> Response>;
130
131             fn call(
132                 self,
133                 ($($ty,)*): ($($ty,)*),
134                 _state: S,
135             ) -> <Self as HandlerCallWithExtractors<($($ty,)*), S>>::Future {
136                 self($($ty,)*).map(IntoResponse::into_response)
137             }
138         }
139     };
140 }
141
142impl_handler_call_with!();
143impl_handler_call_with!(T1);
144impl_handler_call_with!(T1, T2);
145impl_handler_call_with!(T1, T2, T3);
146impl_handler_call_with!(T1, T2, T3, T4);
147impl_handler_call_with!(T1, T2, T3, T4, T5);
148impl_handler_call_with!(T1, T2, T3, T4, T5, T6);
149impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7);
150impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8);
151impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8, T9);
152impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
153impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
154impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
155impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13);
156impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14);
157impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15);
158impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16);
159
160/// A [`Handler`] created from a [`HandlerCallWithExtractors`].
161///
162/// Created with [`HandlerCallWithExtractors::into_handler`].
163#[allow(missing_debug_implementations)]
164pub struct IntoHandler<H, T, S> {
165    handler: H,
166    _marker: PhantomData<fn() -> (T, S)>,
167}
168
169impl<H, T, S> Handler<T, S> for IntoHandler<H, T, S>
170where
171    H: HandlerCallWithExtractors<T, S> + Clone + Send + 'static,
172    T: FromRequest<S> + Send + 'static,
173    T::Rejection: Send,
174    S: Send + Sync + 'static,
175{
176    type Future = BoxFuture<'static, Response>;
177
178    fn call(self, req: Request, state: S) -> Self::Future {
179        let req = req.map(Body::new);
180        Box::pin(async move {
181            match T::from_request(req, &state).await {
182                Ok(t) => self.handler.call(t, state).await,
183                Err(rejection) => rejection.into_response(),
184            }
185        })
186    }
187}
188
189impl<H, T, S> Copy for IntoHandler<H, T, S> where H: Copy {}
190
191impl<H, T, S> Clone for IntoHandler<H, T, S>
192where
193    H: Clone,
194{
195    fn clone(&self) -> Self {
196        Self {
197            handler: self.handler.clone(),
198            _marker: self._marker,
199        }
200    }
201}