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
+87
-225
@@ -4,11 +4,10 @@
|
||||
//!
|
||||
//! [`axum::extract`]: https://docs.rs/axum/latest/axum/extract/index.html
|
||||
|
||||
use self::rejection::*;
|
||||
use crate::response::IntoResponse;
|
||||
use async_trait::async_trait;
|
||||
use http::{Extensions, HeaderMap, Method, Request, Uri, Version};
|
||||
use std::{convert::Infallible, sync::Arc};
|
||||
use http::{request::Parts, Request};
|
||||
use std::convert::Infallible;
|
||||
|
||||
pub mod rejection;
|
||||
|
||||
@@ -18,9 +17,44 @@ mod tuple;
|
||||
|
||||
pub use self::from_ref::FromRef;
|
||||
|
||||
mod private {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum ViaParts {}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum ViaRequest {}
|
||||
}
|
||||
|
||||
/// Types that can be created from request parts.
|
||||
///
|
||||
/// Extractors that implement `FromRequestParts` cannot consume the request body and can thus be
|
||||
/// run in any order for handlers.
|
||||
///
|
||||
/// If your extractor needs to consume the request body then you should implement [`FromRequest`]
|
||||
/// and not [`FromRequestParts`].
|
||||
///
|
||||
/// See [`axum::extract`] for more general docs about extraxtors.
|
||||
///
|
||||
/// [`axum::extract`]: https://docs.rs/axum/0.6/axum/extract/index.html
|
||||
#[async_trait]
|
||||
pub trait FromRequestParts<S>: Sized {
|
||||
/// If the extractor fails it'll use this "rejection" type. A rejection is
|
||||
/// a kind of error that can be converted into a response.
|
||||
type Rejection: IntoResponse;
|
||||
|
||||
/// Perform the extraction.
|
||||
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection>;
|
||||
}
|
||||
|
||||
/// Types that can be created from requests.
|
||||
///
|
||||
/// See [`axum::extract`] for more details.
|
||||
/// Extractors that implement `FromRequest` can consume the request body and can thus only be run
|
||||
/// once for handlers.
|
||||
///
|
||||
/// If your extractor doesn't need to consume the request body then you should implement
|
||||
/// [`FromRequestParts`] and not [`FromRequest`].
|
||||
///
|
||||
/// See [`axum::extract`] for more general docs about extraxtors.
|
||||
///
|
||||
/// # What is the `B` type parameter?
|
||||
///
|
||||
@@ -39,7 +73,8 @@ pub use self::from_ref::FromRef;
|
||||
/// ```rust
|
||||
/// use axum::{
|
||||
/// async_trait,
|
||||
/// extract::{FromRequest, RequestParts},
|
||||
/// extract::FromRequest,
|
||||
/// http::Request,
|
||||
/// };
|
||||
///
|
||||
/// struct MyExtractor;
|
||||
@@ -48,12 +83,12 @@ pub use self::from_ref::FromRef;
|
||||
/// impl<S, B> FromRequest<S, B> for MyExtractor
|
||||
/// where
|
||||
/// // these bounds are required by `async_trait`
|
||||
/// B: Send,
|
||||
/// B: Send + 'static,
|
||||
/// S: Send + Sync,
|
||||
/// {
|
||||
/// type Rejection = http::StatusCode;
|
||||
///
|
||||
/// async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
/// async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
|
||||
/// // ...
|
||||
/// # unimplemented!()
|
||||
/// }
|
||||
@@ -63,231 +98,45 @@ pub use self::from_ref::FromRef;
|
||||
/// This ensures your extractor is as flexible as possible.
|
||||
///
|
||||
/// [`http::Request<B>`]: http::Request
|
||||
/// [`axum::extract`]: https://docs.rs/axum/latest/axum/extract/index.html
|
||||
/// [`axum::extract`]: https://docs.rs/axum/0.6/axum/extract/index.html
|
||||
#[async_trait]
|
||||
pub trait FromRequest<S, B>: Sized {
|
||||
pub trait FromRequest<S, B, M = private::ViaRequest>: Sized {
|
||||
/// If the extractor fails it'll use this "rejection" type. A rejection is
|
||||
/// a kind of error that can be converted into a response.
|
||||
type Rejection: IntoResponse;
|
||||
|
||||
/// Perform the extraction.
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection>;
|
||||
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection>;
|
||||
}
|
||||
|
||||
/// The type used with [`FromRequest`] to extract data from requests.
|
||||
///
|
||||
/// Has several convenience methods for getting owned parts of the request.
|
||||
#[derive(Debug)]
|
||||
pub struct RequestParts<S, B> {
|
||||
pub(crate) state: Arc<S>,
|
||||
method: Method,
|
||||
uri: Uri,
|
||||
version: Version,
|
||||
headers: HeaderMap,
|
||||
extensions: Extensions,
|
||||
body: Option<B>,
|
||||
}
|
||||
#[async_trait]
|
||||
impl<S, B, T> FromRequest<S, B, private::ViaParts> for T
|
||||
where
|
||||
B: Send + 'static,
|
||||
S: Send + Sync,
|
||||
T: FromRequestParts<S>,
|
||||
{
|
||||
type Rejection = <Self as FromRequestParts<S>>::Rejection;
|
||||
|
||||
impl<B> RequestParts<(), B> {
|
||||
/// Create a new `RequestParts` without any state.
|
||||
///
|
||||
/// You generally shouldn't need to construct this type yourself, unless
|
||||
/// using extractors outside of axum for example to implement a
|
||||
/// [`tower::Service`].
|
||||
///
|
||||
/// [`tower::Service`]: https://docs.rs/tower/lastest/tower/trait.Service.html
|
||||
pub fn new(req: Request<B>) -> Self {
|
||||
Self::with_state((), req)
|
||||
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
|
||||
let (mut parts, _) = req.into_parts();
|
||||
Self::from_request_parts(&mut parts, state).await
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, B> RequestParts<S, B> {
|
||||
/// Create a new `RequestParts` with the given state.
|
||||
///
|
||||
/// You generally shouldn't need to construct this type yourself, unless
|
||||
/// using extractors outside of axum for example to implement a
|
||||
/// [`tower::Service`].
|
||||
///
|
||||
/// [`tower::Service`]: https://docs.rs/tower/lastest/tower/trait.Service.html
|
||||
pub fn with_state(state: S, req: Request<B>) -> Self {
|
||||
Self::with_state_arc(Arc::new(state), req)
|
||||
}
|
||||
#[async_trait]
|
||||
impl<S, T> FromRequestParts<S> for Option<T>
|
||||
where
|
||||
T: FromRequestParts<S>,
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
|
||||
/// Create a new `RequestParts` with the given [`Arc`]'ed state.
|
||||
///
|
||||
/// You generally shouldn't need to construct this type yourself, unless
|
||||
/// using extractors outside of axum for example to implement a
|
||||
/// [`tower::Service`].
|
||||
///
|
||||
/// [`tower::Service`]: https://docs.rs/tower/lastest/tower/trait.Service.html
|
||||
pub fn with_state_arc(state: Arc<S>, req: Request<B>) -> Self {
|
||||
let (
|
||||
http::request::Parts {
|
||||
method,
|
||||
uri,
|
||||
version,
|
||||
headers,
|
||||
extensions,
|
||||
..
|
||||
},
|
||||
body,
|
||||
) = req.into_parts();
|
||||
|
||||
RequestParts {
|
||||
state,
|
||||
method,
|
||||
uri,
|
||||
version,
|
||||
headers,
|
||||
extensions,
|
||||
body: Some(body),
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply an extractor to this `RequestParts`.
|
||||
///
|
||||
/// `req.extract::<Extractor>()` is equivalent to `Extractor::from_request(req)`.
|
||||
/// This function simply exists as a convenience.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # struct MyExtractor {}
|
||||
///
|
||||
/// use std::convert::Infallible;
|
||||
///
|
||||
/// use async_trait::async_trait;
|
||||
/// use axum::extract::{FromRequest, RequestParts};
|
||||
/// use http::{Method, Uri};
|
||||
///
|
||||
/// #[async_trait]
|
||||
/// impl<S, B> FromRequest<S, B> for MyExtractor
|
||||
/// where
|
||||
/// B: Send,
|
||||
/// S: Send + Sync,
|
||||
/// {
|
||||
/// type Rejection = Infallible;
|
||||
///
|
||||
/// async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Infallible> {
|
||||
/// let method = req.extract::<Method>().await?;
|
||||
/// let path = req.extract::<Uri>().await?.path().to_owned();
|
||||
///
|
||||
/// todo!()
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn extract<E>(&mut self) -> Result<E, E::Rejection>
|
||||
where
|
||||
E: FromRequest<S, B>,
|
||||
{
|
||||
E::from_request(self).await
|
||||
}
|
||||
|
||||
/// Convert this `RequestParts` back into a [`Request`].
|
||||
///
|
||||
/// Fails if The request body has been extracted, that is [`take_body`] has
|
||||
/// been called.
|
||||
///
|
||||
/// [`take_body`]: RequestParts::take_body
|
||||
pub fn try_into_request(self) -> Result<Request<B>, BodyAlreadyExtracted> {
|
||||
let Self {
|
||||
state: _,
|
||||
method,
|
||||
uri,
|
||||
version,
|
||||
headers,
|
||||
extensions,
|
||||
mut body,
|
||||
} = self;
|
||||
|
||||
let mut req = if let Some(body) = body.take() {
|
||||
Request::new(body)
|
||||
} else {
|
||||
return Err(BodyAlreadyExtracted);
|
||||
};
|
||||
|
||||
*req.method_mut() = method;
|
||||
*req.uri_mut() = uri;
|
||||
*req.version_mut() = version;
|
||||
*req.headers_mut() = headers;
|
||||
*req.extensions_mut() = extensions;
|
||||
|
||||
Ok(req)
|
||||
}
|
||||
|
||||
/// Gets a reference to the request method.
|
||||
pub fn method(&self) -> &Method {
|
||||
&self.method
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the request method.
|
||||
pub fn method_mut(&mut self) -> &mut Method {
|
||||
&mut self.method
|
||||
}
|
||||
|
||||
/// Gets a reference to the request URI.
|
||||
pub fn uri(&self) -> &Uri {
|
||||
&self.uri
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the request URI.
|
||||
pub fn uri_mut(&mut self) -> &mut Uri {
|
||||
&mut self.uri
|
||||
}
|
||||
|
||||
/// Get the request HTTP version.
|
||||
pub fn version(&self) -> Version {
|
||||
self.version
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the request HTTP version.
|
||||
pub fn version_mut(&mut self) -> &mut Version {
|
||||
&mut self.version
|
||||
}
|
||||
|
||||
/// Gets a reference to the request headers.
|
||||
pub fn headers(&self) -> &HeaderMap {
|
||||
&self.headers
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the request headers.
|
||||
pub fn headers_mut(&mut self) -> &mut HeaderMap {
|
||||
&mut self.headers
|
||||
}
|
||||
|
||||
/// Gets a reference to the request extensions.
|
||||
pub fn extensions(&self) -> &Extensions {
|
||||
&self.extensions
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the request extensions.
|
||||
pub fn extensions_mut(&mut self) -> &mut Extensions {
|
||||
&mut self.extensions
|
||||
}
|
||||
|
||||
/// Gets a reference to the request body.
|
||||
///
|
||||
/// Returns `None` if the body has been taken by another extractor.
|
||||
pub fn body(&self) -> Option<&B> {
|
||||
self.body.as_ref()
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the request body.
|
||||
///
|
||||
/// Returns `None` if the body has been taken by another extractor.
|
||||
// this returns `&mut Option<B>` rather than `Option<&mut B>` such that users can use it to set the body.
|
||||
pub fn body_mut(&mut self) -> &mut Option<B> {
|
||||
&mut self.body
|
||||
}
|
||||
|
||||
/// Takes the body out of the request, leaving a `None` in its place.
|
||||
pub fn take_body(&mut self) -> Option<B> {
|
||||
self.body.take()
|
||||
}
|
||||
|
||||
/// Get a reference to the state.
|
||||
pub fn state(&self) -> &S {
|
||||
&self.state
|
||||
async fn from_request_parts(
|
||||
parts: &mut Parts,
|
||||
state: &S,
|
||||
) -> Result<Option<T>, Self::Rejection> {
|
||||
Ok(T::from_request_parts(parts, state).await.ok())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,13 +144,26 @@ impl<S, B> RequestParts<S, B> {
|
||||
impl<S, T, B> FromRequest<S, B> for Option<T>
|
||||
where
|
||||
T: FromRequest<S, B>,
|
||||
B: Send,
|
||||
B: Send + 'static,
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Option<T>, Self::Rejection> {
|
||||
Ok(T::from_request(req).await.ok())
|
||||
async fn from_request(req: Request<B>, state: &S) -> Result<Option<T>, Self::Rejection> {
|
||||
Ok(T::from_request(req, state).await.ok())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<S, T> FromRequestParts<S> for Result<T, T::Rejection>
|
||||
where
|
||||
T: FromRequestParts<S>,
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
|
||||
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
|
||||
Ok(T::from_request_parts(parts, state).await)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,12 +171,12 @@ where
|
||||
impl<S, T, B> FromRequest<S, B> for Result<T, T::Rejection>
|
||||
where
|
||||
T: FromRequest<S, B>,
|
||||
B: Send,
|
||||
B: Send + 'static,
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
Ok(T::from_request(req).await)
|
||||
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
|
||||
Ok(T::from_request(req, state).await)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,6 @@
|
||||
//! Rejection response types.
|
||||
|
||||
use crate::{
|
||||
response::{IntoResponse, Response},
|
||||
BoxError,
|
||||
};
|
||||
use http::StatusCode;
|
||||
use std::fmt;
|
||||
|
||||
/// Rejection type used if you try and extract the request body more than
|
||||
/// once.
|
||||
#[derive(Debug, Default)]
|
||||
#[non_exhaustive]
|
||||
pub struct BodyAlreadyExtracted;
|
||||
|
||||
impl BodyAlreadyExtracted {
|
||||
const BODY: &'static str = "Cannot have two request body extractors for a single handler";
|
||||
}
|
||||
|
||||
impl IntoResponse for BodyAlreadyExtracted {
|
||||
fn into_response(self) -> Response {
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, Self::BODY).into_response()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BodyAlreadyExtracted {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", Self::BODY)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for BodyAlreadyExtracted {}
|
||||
use crate::BoxError;
|
||||
|
||||
composite_rejection! {
|
||||
/// Rejection type for extractors that buffer the request body. Used if the
|
||||
@@ -85,7 +56,6 @@ composite_rejection! {
|
||||
/// Contains one variant for each way the [`Bytes`](bytes::Bytes) extractor
|
||||
/// can fail.
|
||||
pub enum BytesRejection {
|
||||
BodyAlreadyExtracted,
|
||||
FailedToBufferBody,
|
||||
}
|
||||
}
|
||||
@@ -95,7 +65,6 @@ composite_rejection! {
|
||||
///
|
||||
/// Contains one variant for each way the [`String`] extractor can fail.
|
||||
pub enum StringRejection {
|
||||
BodyAlreadyExtracted,
|
||||
FailedToBufferBody,
|
||||
InvalidUtf8,
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use super::{rejection::*, FromRequest, RequestParts};
|
||||
use super::{rejection::*, FromRequest, FromRequestParts};
|
||||
use crate::BoxError;
|
||||
use async_trait::async_trait;
|
||||
use bytes::Bytes;
|
||||
use http::{Extensions, HeaderMap, Method, Request, Uri, Version};
|
||||
use std::{convert::Infallible, sync::Arc};
|
||||
use http::{request::Parts, HeaderMap, Method, Request, Uri, Version};
|
||||
use std::convert::Infallible;
|
||||
|
||||
#[async_trait]
|
||||
impl<S, B> FromRequest<S, B> for Request<B>
|
||||
@@ -11,62 +11,46 @@ where
|
||||
B: Send,
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = BodyAlreadyExtracted;
|
||||
type Rejection = Infallible;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
let req = std::mem::replace(
|
||||
req,
|
||||
RequestParts {
|
||||
state: Arc::clone(&req.state),
|
||||
method: req.method.clone(),
|
||||
version: req.version,
|
||||
uri: req.uri.clone(),
|
||||
headers: HeaderMap::new(),
|
||||
extensions: Extensions::default(),
|
||||
body: None,
|
||||
},
|
||||
);
|
||||
|
||||
req.try_into_request()
|
||||
async fn from_request(req: Request<B>, _: &S) -> Result<Self, Self::Rejection> {
|
||||
Ok(req)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<S, B> FromRequest<S, B> for Method
|
||||
impl<S> FromRequestParts<S> for Method
|
||||
where
|
||||
B: Send,
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
Ok(req.method().clone())
|
||||
async fn from_request_parts(parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
|
||||
Ok(parts.method.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<S, B> FromRequest<S, B> for Uri
|
||||
impl<S> FromRequestParts<S> for Uri
|
||||
where
|
||||
B: Send,
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
Ok(req.uri().clone())
|
||||
async fn from_request_parts(parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
|
||||
Ok(parts.uri.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<S, B> FromRequest<S, B> for Version
|
||||
impl<S> FromRequestParts<S> for Version
|
||||
where
|
||||
B: Send,
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
Ok(req.version())
|
||||
async fn from_request_parts(parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
|
||||
Ok(parts.version)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,30 +60,29 @@ where
|
||||
///
|
||||
/// [`TypedHeader`]: https://docs.rs/axum/latest/axum/extract/struct.TypedHeader.html
|
||||
#[async_trait]
|
||||
impl<S, B> FromRequest<S, B> for HeaderMap
|
||||
impl<S> FromRequestParts<S> for HeaderMap
|
||||
where
|
||||
B: Send,
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
Ok(req.headers().clone())
|
||||
async fn from_request_parts(parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
|
||||
Ok(parts.headers.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<S, B> FromRequest<S, B> for Bytes
|
||||
where
|
||||
B: http_body::Body + Send,
|
||||
B: http_body::Body + Send + 'static,
|
||||
B::Data: Send,
|
||||
B::Error: Into<BoxError>,
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = BytesRejection;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
let body = take_body(req)?;
|
||||
async fn from_request(req: Request<B>, _: &S) -> Result<Self, Self::Rejection> {
|
||||
let body = req.into_body();
|
||||
|
||||
let bytes = crate::body::to_bytes(body)
|
||||
.await
|
||||
@@ -112,15 +95,15 @@ where
|
||||
#[async_trait]
|
||||
impl<S, B> FromRequest<S, B> for String
|
||||
where
|
||||
B: http_body::Body + Send,
|
||||
B: http_body::Body + Send + 'static,
|
||||
B::Data: Send,
|
||||
B::Error: Into<BoxError>,
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = StringRejection;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
let body = take_body(req)?;
|
||||
async fn from_request(req: Request<B>, _: &S) -> Result<Self, Self::Rejection> {
|
||||
let body = req.into_body();
|
||||
|
||||
let bytes = crate::body::to_bytes(body)
|
||||
.await
|
||||
@@ -134,40 +117,14 @@ where
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<S, B> FromRequest<S, B> for http::request::Parts
|
||||
impl<S, B> FromRequest<S, B> for Parts
|
||||
where
|
||||
B: Send,
|
||||
B: Send + 'static,
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
let method = unwrap_infallible(Method::from_request(req).await);
|
||||
let uri = unwrap_infallible(Uri::from_request(req).await);
|
||||
let version = unwrap_infallible(Version::from_request(req).await);
|
||||
let headers = unwrap_infallible(HeaderMap::from_request(req).await);
|
||||
let extensions = std::mem::take(req.extensions_mut());
|
||||
|
||||
let mut temp_request = Request::new(());
|
||||
*temp_request.method_mut() = method;
|
||||
*temp_request.uri_mut() = uri;
|
||||
*temp_request.version_mut() = version;
|
||||
*temp_request.headers_mut() = headers;
|
||||
*temp_request.extensions_mut() = extensions;
|
||||
|
||||
let (parts, _) = temp_request.into_parts();
|
||||
|
||||
Ok(parts)
|
||||
async fn from_request(req: Request<B>, _: &S) -> Result<Self, Self::Rejection> {
|
||||
Ok(req.into_parts().0)
|
||||
}
|
||||
}
|
||||
|
||||
fn unwrap_infallible<T>(result: Result<T, Infallible>) -> T {
|
||||
match result {
|
||||
Ok(value) => value,
|
||||
Err(err) => match err {},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn take_body<S, B>(req: &mut RequestParts<S, B>) -> Result<B, BodyAlreadyExtracted> {
|
||||
req.take_body().ok_or(BodyAlreadyExtracted)
|
||||
}
|
||||
|
||||
+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