mirror of
https://github.com/tokio-rs/axum.git
synced 2026-08-28 00:00:20 +02:00
Only allow last extractor to mutate the request (#1272)
* Only allow last extractor to mutate the request * Change `FromRequest` and add `FromRequestParts` trait (#1275) * Add `Once`/`Mut` type parameter for `FromRequest` and `RequestParts` * 🪄 * split traits * `FromRequest` for tuples * Remove `BodyAlreadyExtracted` * don't need fully qualified path * don't export `Once` and `Mut` * remove temp tests * depend on axum again Co-authored-by: Jonas Platte <[email protected]> * Port `Handler` and most extractors (#1277) * Port `Handler` and most extractors * Put `M` inside `Handler` impls, not trait itself * comment out tuples for now * fix lints * Reorder arguments to `Handler` (#1281) I think `Request<B>, Arc<S>` is better since its consistent with `FromRequest` and `FromRequestParts`. * Port most things in axum-extra (#1282) * Port `#[derive(TypedPath)]` and `#[debug_handler]` (#1283) * port #[derive(TypedPath)] * wip: #[debug_handler] * fix #[debug_handler] * don't need itertools * also require `Send` * update expected error * support fully qualified `self` * Implement FromRequest[Parts] for tuples (#1286) * Port docs for axum and axum-core (#1285) * Port axum-extra (#1287) * Port axum-extra * Update axum-core/Cargo.toml Co-authored-by: Jonas Platte <[email protected]> * remove `impl FromRequest for Either*` Co-authored-by: Jonas Platte <[email protected]> * New FromRequest[Parts] trait cleanup (#1288) * Make private module truly private again * Simplify tuple FromRequest implementation * Port `#[derive(FromRequest)]` (#1289) * fix tests * fix docs * revert examples * fix docs link * fix intra docs links * Port examples (#1291) * Document wrapping other extractors (#1292) * axum-extra doesn't need to depend on axum-core (#1294) Missed this in https://github.com/tokio-rs/axum/pull/1287 * Add `FromRequest` changes to changelogs (#1293) * Update changelog * Remove default type for `S` in `Handler` * Clarify which types have default types for `S` * Apply suggestions from code review Co-authored-by: Jonas Platte <[email protected]> Co-authored-by: Jonas Platte <[email protected]> * remove unused import * Rename `Mut` and `Once` (#1296) * fix trybuild expected output Co-authored-by: Jonas Platte <[email protected]>
This commit is contained in:
co-authored by
Jonas Platte
parent
f1769e5134
commit
be624306f4
+117
-15
@@ -1,41 +1,143 @@
|
||||
use super::{FromRequest, RequestParts};
|
||||
use super::{FromRequest, FromRequestParts};
|
||||
use crate::response::{IntoResponse, Response};
|
||||
use async_trait::async_trait;
|
||||
use http::request::{Parts, Request};
|
||||
use std::convert::Infallible;
|
||||
|
||||
#[async_trait]
|
||||
impl<S, B> FromRequest<S, B> for ()
|
||||
impl<S> FromRequestParts<S> for ()
|
||||
where
|
||||
B: Send,
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
|
||||
async fn from_request(_: &mut RequestParts<S, B>) -> Result<(), Self::Rejection> {
|
||||
async fn from_request_parts(_: &mut Parts, _: &S) -> Result<(), Self::Rejection> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_from_request {
|
||||
() => {};
|
||||
|
||||
( $($ty:ident),* $(,)? ) => {
|
||||
(
|
||||
[$($ty:ident),*], $last:ident
|
||||
) => {
|
||||
#[async_trait]
|
||||
#[allow(non_snake_case)]
|
||||
impl<S, B, $($ty,)*> FromRequest<S, B> for ($($ty,)*)
|
||||
#[allow(non_snake_case, unused_mut, unused_variables)]
|
||||
impl<S, $($ty,)* $last> FromRequestParts<S> for ($($ty,)* $last,)
|
||||
where
|
||||
$( $ty: FromRequest<S, B> + Send, )*
|
||||
B: Send,
|
||||
$( $ty: FromRequestParts<S> + Send, )*
|
||||
$last: FromRequestParts<S> + Send,
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = Response;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
$( let $ty = $ty::from_request(req).await.map_err(|err| err.into_response())?; )*
|
||||
Ok(($($ty,)*))
|
||||
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
|
||||
$(
|
||||
let $ty = $ty::from_request_parts(parts, state)
|
||||
.await
|
||||
.map_err(|err| err.into_response())?;
|
||||
)*
|
||||
let $last = $last::from_request_parts(parts, state)
|
||||
.await
|
||||
.map_err(|err| err.into_response())?;
|
||||
|
||||
Ok(($($ty,)* $last,))
|
||||
}
|
||||
}
|
||||
|
||||
// This impl must not be generic over M, otherwise it would conflict with the blanket
|
||||
// implementation of `FromRequest<S, B, Mut>` for `T: FromRequestParts<S>`.
|
||||
#[async_trait]
|
||||
#[allow(non_snake_case, unused_mut, unused_variables)]
|
||||
impl<S, B, $($ty,)* $last> FromRequest<S, B> for ($($ty,)* $last,)
|
||||
where
|
||||
$( $ty: FromRequestParts<S> + Send, )*
|
||||
$last: FromRequest<S, B> + Send,
|
||||
B: Send + 'static,
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = Response;
|
||||
|
||||
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
|
||||
let (mut parts, body) = req.into_parts();
|
||||
|
||||
$(
|
||||
let $ty = $ty::from_request_parts(&mut parts, state).await.map_err(|err| err.into_response())?;
|
||||
)*
|
||||
|
||||
let req = Request::from_parts(parts, body);
|
||||
|
||||
let $last = $last::from_request(req, state).await.map_err(|err| err.into_response())?;
|
||||
|
||||
Ok(($($ty,)* $last,))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
all_the_tuples!(impl_from_request);
|
||||
impl_from_request!([], T1);
|
||||
impl_from_request!([T1], T2);
|
||||
impl_from_request!([T1, T2], T3);
|
||||
impl_from_request!([T1, T2, T3], T4);
|
||||
impl_from_request!([T1, T2, T3, T4], T5);
|
||||
impl_from_request!([T1, T2, T3, T4, T5], T6);
|
||||
impl_from_request!([T1, T2, T3, T4, T5, T6], T7);
|
||||
impl_from_request!([T1, T2, T3, T4, T5, T6, T7], T8);
|
||||
impl_from_request!([T1, T2, T3, T4, T5, T6, T7, T8], T9);
|
||||
impl_from_request!([T1, T2, T3, T4, T5, T6, T7, T8, T9], T10);
|
||||
impl_from_request!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T11);
|
||||
impl_from_request!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], T12);
|
||||
impl_from_request!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], T13);
|
||||
impl_from_request!(
|
||||
[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13],
|
||||
T14
|
||||
);
|
||||
impl_from_request!(
|
||||
[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14],
|
||||
T15
|
||||
);
|
||||
impl_from_request!(
|
||||
[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15],
|
||||
T16
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use bytes::Bytes;
|
||||
use http::Method;
|
||||
|
||||
use crate::extract::{FromRequest, FromRequestParts};
|
||||
|
||||
fn assert_from_request<M, T>()
|
||||
where
|
||||
T: FromRequest<(), http_body::Full<Bytes>, M>,
|
||||
{
|
||||
}
|
||||
|
||||
fn assert_from_request_parts<T: FromRequestParts<()>>() {}
|
||||
|
||||
#[test]
|
||||
fn unit() {
|
||||
assert_from_request_parts::<()>();
|
||||
assert_from_request::<_, ()>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tuple_of_one() {
|
||||
assert_from_request_parts::<(Method,)>();
|
||||
assert_from_request::<_, (Method,)>();
|
||||
assert_from_request::<_, (Bytes,)>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tuple_of_two() {
|
||||
assert_from_request_parts::<((), ())>();
|
||||
assert_from_request::<_, ((), ())>();
|
||||
assert_from_request::<_, (Method, Bytes)>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_tuple() {
|
||||
assert_from_request_parts::<(((Method,),),)>();
|
||||
assert_from_request::<_, ((((Bytes,),),),)>();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user