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    ///     routing::get,
51    ///     extract::FromRequestParts,
52    /// };
53    ///
54    /// // handlers for varying levels of access
55    /// async fn admin(admin: AdminPermissions) {
56    ///     // request came from an admin
57    /// }
58    ///
59    /// async fn user(user: User) {
60    ///     // we have a `User`
61    /// }
62    ///
63    /// async fn guest() {
64    ///     // `AdminPermissions` and `User` failed, so we're just a guest
65    /// }
66    ///
67    /// // extractors for checking permissions
68    /// struct AdminPermissions {}
69    ///
70    /// impl<S> FromRequestParts<S> for AdminPermissions
71    /// where
72    ///     S: Send + Sync,
73    /// {
74    ///     // check for admin permissions...
75    ///     # type Rejection = ();
76    ///     # async fn from_request_parts(parts: &mut http::request::Parts, state: &S) -> Result<Self, Self::Rejection> {
77    ///     #     todo!()
78    ///     # }
79    /// }
80    ///
81    /// struct User {}
82    ///
83    /// impl<S> FromRequestParts<S> for User
84    /// where
85    ///     S: Send + Sync,
86    /// {
87    ///     // check for a logged in user...
88    ///     # type Rejection = ();
89    ///     # async fn from_request_parts(parts: &mut http::request::Parts, state: &S) -> Result<Self, Self::Rejection> {
90    ///     #     todo!()
91    ///     # }
92    /// }
93    ///
94    /// let app = Router::new().route(
95    ///     "/users/{id}",
96    ///     get(
97    ///         // first try `admin`, if that rejects run `user`, finally falling back
98    ///         // to `guest`
99    ///         admin.or(user).or(guest)
100    ///     )
101    /// );
102    /// # let _: Router = app;
103    /// ```
104    fn or<R, Rt>(self, rhs: R) -> Or<Self, R, T, Rt, S>
105    where
106        R: HandlerCallWithExtractors<Rt, S>,
107    {
108        Or {
109            lhs: self,
110            rhs,
111            _marker: PhantomData,
112        }
113    }
114}
115
116macro_rules! impl_handler_call_with {
117     ( $($ty:ident),* $(,)? ) => {
118         #[allow(non_snake_case)]
119         impl<F, Fut, S, $($ty,)*> HandlerCallWithExtractors<($($ty,)*), S> for F
120         where
121             F: FnOnce($($ty,)*) -> Fut,
122             Fut: Future + Send + 'static,
123             Fut::Output: IntoResponse,
124         {
125             // this puts `futures_util` in our public API but thats fine in axum-extra
126             type Future = Map<Fut, fn(Fut::Output) -> Response>;
127
128             fn call(
129                 self,
130                 ($($ty,)*): ($($ty,)*),
131                 _state: S,
132             ) -> <Self as HandlerCallWithExtractors<($($ty,)*), S>>::Future {
133                 self($($ty,)*).map(IntoResponse::into_response)
134             }
135         }
136     };
137 }
138
139impl_handler_call_with!();
140impl_handler_call_with!(T1);
141impl_handler_call_with!(T1, T2);
142impl_handler_call_with!(T1, T2, T3);
143impl_handler_call_with!(T1, T2, T3, T4);
144impl_handler_call_with!(T1, T2, T3, T4, T5);
145impl_handler_call_with!(T1, T2, T3, T4, T5, T6);
146impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7);
147impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8);
148impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8, T9);
149impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
150impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
151impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
152impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13);
153impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14);
154impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15);
155impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16);
156
157/// A [`Handler`] created from a [`HandlerCallWithExtractors`].
158///
159/// Created with [`HandlerCallWithExtractors::into_handler`].
160#[allow(missing_debug_implementations)]
161pub struct IntoHandler<H, T, S> {
162    handler: H,
163    _marker: PhantomData<fn() -> (T, S)>,
164}
165
166impl<H, T, S> Handler<T, S> for IntoHandler<H, T, S>
167where
168    H: HandlerCallWithExtractors<T, S> + Clone + Send + Sync + 'static,
169    T: FromRequest<S> + Send + 'static,
170    T::Rejection: Send,
171    S: Send + Sync + 'static,
172{
173    type Future = BoxFuture<'static, Response>;
174
175    fn call(self, req: Request, state: S) -> Self::Future {
176        let req = req.map(Body::new);
177        Box::pin(async move {
178            match T::from_request(req, &state).await {
179                Ok(t) => self.handler.call(t, state).await,
180                Err(rejection) => rejection.into_response(),
181            }
182        })
183    }
184}
185
186impl<H, T, S> Copy for IntoHandler<H, T, S> where H: Copy {}
187
188impl<H, T, S> Clone for IntoHandler<H, T, S>
189where
190    H: Clone,
191{
192    fn clone(&self) -> Self {
193        Self {
194            handler: self.handler.clone(),
195            _marker: self._marker,
196        }
197    }
198}