mirror of
https://github.com/tokio-rs/axum.git
synced 2026-08-25 00:00:23 +02:00
Make extractors easier to write (#36)
Previously extractors worked directly on `Request<B>` which meant you had to do weird tricks like `mem::take(req.headers_mut())` to get owned parts of the request. This changes that instead to use a new `RequestParts` type that have methods to "take" each part of the request. Without having to do weird tricks. Also removed the need to have `B: Default` for body extractors.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
//!
|
||||
//! See [`extractor_middleware`] for more details.
|
||||
|
||||
use super::FromRequest;
|
||||
use super::{FromRequest, RequestParts};
|
||||
use crate::{body::BoxBody, response::IntoResponse};
|
||||
use bytes::Bytes;
|
||||
use futures_util::{future::BoxFuture, ready};
|
||||
@@ -34,7 +34,7 @@ use tower::{BoxError, Layer, Service};
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use axum::{extract::extractor_middleware, prelude::*};
|
||||
/// use axum::{extract::{extractor_middleware, RequestParts}, prelude::*};
|
||||
/// use http::StatusCode;
|
||||
/// use async_trait::async_trait;
|
||||
///
|
||||
@@ -48,12 +48,13 @@ use tower::{BoxError, Layer, Service};
|
||||
/// {
|
||||
/// type Rejection = StatusCode;
|
||||
///
|
||||
/// async fn from_request(req: &mut Request<B>) -> Result<Self, Self::Rejection> {
|
||||
/// if let Some(value) = req
|
||||
/// async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
/// let auth_header = req
|
||||
/// .headers()
|
||||
/// .get(http::header::AUTHORIZATION)
|
||||
/// .and_then(|value| value.to_str().ok())
|
||||
/// {
|
||||
/// .and_then(|headers| headers.get(http::header::AUTHORIZATION))
|
||||
/// .and_then(|value| value.to_str().ok());
|
||||
///
|
||||
/// if let Some(value) = auth_header {
|
||||
/// if value == "secret" {
|
||||
/// return Ok(Self);
|
||||
/// }
|
||||
@@ -169,8 +170,9 @@ where
|
||||
self.inner.poll_ready(cx)
|
||||
}
|
||||
|
||||
fn call(&mut self, mut req: Request<ReqBody>) -> Self::Future {
|
||||
fn call(&mut self, req: Request<ReqBody>) -> Self::Future {
|
||||
let extract_future = Box::pin(async move {
|
||||
let mut req = super::RequestParts::new(req);
|
||||
let extracted = E::from_request(&mut req).await;
|
||||
(req, extracted)
|
||||
});
|
||||
@@ -201,7 +203,7 @@ where
|
||||
E: FromRequest<ReqBody>,
|
||||
S: Service<Request<ReqBody>>,
|
||||
{
|
||||
Extracting(BoxFuture<'static, (Request<ReqBody>, Result<E, E::Rejection>)>),
|
||||
Extracting(BoxFuture<'static, (RequestParts<ReqBody>, Result<E, E::Rejection>)>),
|
||||
Call(#[pin] S::Future),
|
||||
}
|
||||
|
||||
@@ -220,16 +222,16 @@ where
|
||||
|
||||
let new_state = match this.state.as_mut().project() {
|
||||
StateProj::Extracting(future) => {
|
||||
let (req, extracted) = ready!(future.as_mut().poll(cx));
|
||||
let (mut req, extracted) = ready!(future.as_mut().poll(cx));
|
||||
|
||||
match extracted {
|
||||
Ok(_) => {
|
||||
let mut svc = this.svc.take().expect("future polled after completion");
|
||||
let future = svc.call(req);
|
||||
let future = svc.call(req.into_request());
|
||||
State::Call(future)
|
||||
}
|
||||
Err(err) => {
|
||||
let res = err.into_response().map(BoxBody::new);
|
||||
let res = err.into_response().map(crate::body::box_body);
|
||||
return Poll::Ready(Ok(res));
|
||||
}
|
||||
}
|
||||
@@ -237,7 +239,7 @@ where
|
||||
StateProj::Call(future) => {
|
||||
return future
|
||||
.poll(cx)
|
||||
.map(|result| result.map(|response| response.map(BoxBody::new)));
|
||||
.map(|result| result.map(|response| response.map(crate::body::box_body)));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+283
-79
@@ -34,7 +34,7 @@
|
||||
//! You can also define your own extractors by implementing [`FromRequest`]:
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! use axum::{async_trait, extract::FromRequest, prelude::*};
|
||||
//! use axum::{async_trait, extract::{FromRequest, RequestParts}, prelude::*};
|
||||
//! use http::{StatusCode, header::{HeaderValue, USER_AGENT}};
|
||||
//!
|
||||
//! struct ExtractUserAgent(HeaderValue);
|
||||
@@ -46,8 +46,10 @@
|
||||
//! {
|
||||
//! type Rejection = (StatusCode, &'static str);
|
||||
//!
|
||||
//! async fn from_request(req: &mut Request<B>) -> Result<Self, Self::Rejection> {
|
||||
//! if let Some(user_agent) = req.headers().get(USER_AGENT) {
|
||||
//! async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
//! let user_agent = req.headers().and_then(|headers| headers.get(USER_AGENT));
|
||||
//!
|
||||
//! if let Some(user_agent) = user_agent {
|
||||
//! Ok(ExtractUserAgent(user_agent.clone()))
|
||||
//! } else {
|
||||
//! Err((StatusCode::BAD_REQUEST, "`User-Agent` header is missing"))
|
||||
@@ -175,13 +177,12 @@ use crate::{response::IntoResponse, util::ByteStr};
|
||||
use async_trait::async_trait;
|
||||
use bytes::{Buf, Bytes};
|
||||
use futures_util::stream::Stream;
|
||||
use http::{header, HeaderMap, Method, Request, Uri, Version};
|
||||
use http::{header, Extensions, HeaderMap, Method, Request, Uri, Version};
|
||||
use rejection::*;
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
convert::Infallible,
|
||||
mem,
|
||||
pin::Pin,
|
||||
str::FromStr,
|
||||
task::{Context, Poll},
|
||||
@@ -212,7 +213,195 @@ pub trait FromRequest<B>: Sized {
|
||||
type Rejection: IntoResponse;
|
||||
|
||||
/// Perform the extraction.
|
||||
async fn from_request(req: &mut Request<B>) -> Result<Self, Self::Rejection>;
|
||||
async fn from_request(req: &mut RequestParts<B>) -> 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<B> {
|
||||
method: Option<Method>,
|
||||
uri: Option<Uri>,
|
||||
version: Option<Version>,
|
||||
headers: Option<HeaderMap>,
|
||||
extensions: Option<Extensions>,
|
||||
body: Option<B>,
|
||||
}
|
||||
|
||||
impl<B> RequestParts<B> {
|
||||
pub(crate) fn new(req: Request<B>) -> Self {
|
||||
let (
|
||||
http::request::Parts {
|
||||
method,
|
||||
uri,
|
||||
version,
|
||||
headers,
|
||||
extensions,
|
||||
..
|
||||
},
|
||||
body,
|
||||
) = req.into_parts();
|
||||
|
||||
RequestParts {
|
||||
method: Some(method),
|
||||
uri: Some(uri),
|
||||
version: Some(version),
|
||||
headers: Some(headers),
|
||||
extensions: Some(extensions),
|
||||
body: Some(body),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
pub(crate) fn into_request(&mut self) -> Request<B> {
|
||||
let Self {
|
||||
method,
|
||||
uri,
|
||||
version,
|
||||
headers,
|
||||
extensions,
|
||||
body,
|
||||
} = self;
|
||||
|
||||
let mut req = Request::new(body.take().expect("body already extracted"));
|
||||
|
||||
if let Some(method) = method.take() {
|
||||
*req.method_mut() = method;
|
||||
}
|
||||
|
||||
if let Some(uri) = uri.take() {
|
||||
*req.uri_mut() = uri;
|
||||
}
|
||||
|
||||
if let Some(version) = version.take() {
|
||||
*req.version_mut() = version;
|
||||
}
|
||||
|
||||
if let Some(headers) = headers.take() {
|
||||
*req.headers_mut() = headers;
|
||||
}
|
||||
|
||||
if let Some(extensions) = extensions.take() {
|
||||
*req.extensions_mut() = extensions;
|
||||
}
|
||||
|
||||
req
|
||||
}
|
||||
|
||||
/// Gets a reference to the request method.
|
||||
///
|
||||
/// Returns `None` if the method has been taken by another extractor.
|
||||
pub fn method(&self) -> Option<&Method> {
|
||||
self.method.as_ref()
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the request method.
|
||||
///
|
||||
/// Returns `None` if the method has been taken by another extractor.
|
||||
pub fn method_mut(&mut self) -> Option<&mut Method> {
|
||||
self.method.as_mut()
|
||||
}
|
||||
|
||||
/// Takes the method out of the request, leaving a `None` in its place.
|
||||
pub fn take_method(&mut self) -> Option<Method> {
|
||||
self.method.take()
|
||||
}
|
||||
|
||||
/// Gets a reference to the request URI.
|
||||
///
|
||||
/// Returns `None` if the URI has been taken by another extractor.
|
||||
pub fn uri(&self) -> Option<&Uri> {
|
||||
self.uri.as_ref()
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the request URI.
|
||||
///
|
||||
/// Returns `None` if the URI has been taken by another extractor.
|
||||
pub fn uri_mut(&mut self) -> Option<&mut Uri> {
|
||||
self.uri.as_mut()
|
||||
}
|
||||
|
||||
/// Takes the URI out of the request, leaving a `None` in its place.
|
||||
pub fn take_uri(&mut self) -> Option<Uri> {
|
||||
self.uri.take()
|
||||
}
|
||||
|
||||
/// Gets a reference to the request HTTP version.
|
||||
///
|
||||
/// Returns `None` if the HTTP version has been taken by another extractor.
|
||||
pub fn version(&self) -> Option<Version> {
|
||||
self.version
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the request HTTP version.
|
||||
///
|
||||
/// Returns `None` if the HTTP version has been taken by another extractor.
|
||||
pub fn version_mut(&mut self) -> Option<&mut Version> {
|
||||
self.version.as_mut()
|
||||
}
|
||||
|
||||
/// Takes the HTTP version out of the request, leaving a `None` in its place.
|
||||
pub fn take_version(&mut self) -> Option<Version> {
|
||||
self.version.take()
|
||||
}
|
||||
|
||||
/// Gets a reference to the request headers.
|
||||
///
|
||||
/// Returns `None` if the headers has been taken by another extractor.
|
||||
pub fn headers(&self) -> Option<&HeaderMap> {
|
||||
self.headers.as_ref()
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the request headers.
|
||||
///
|
||||
/// Returns `None` if the headers has been taken by another extractor.
|
||||
pub fn headers_mut(&mut self) -> Option<&mut HeaderMap> {
|
||||
self.headers.as_mut()
|
||||
}
|
||||
|
||||
/// Takes the headers out of the request, leaving a `None` in its place.
|
||||
pub fn take_headers(&mut self) -> Option<HeaderMap> {
|
||||
self.headers.take()
|
||||
}
|
||||
|
||||
/// Gets a reference to the request extensions.
|
||||
///
|
||||
/// Returns `None` if the extensions has been taken by another extractor.
|
||||
pub fn extensions(&self) -> Option<&Extensions> {
|
||||
self.extensions.as_ref()
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the request extensions.
|
||||
///
|
||||
/// Returns `None` if the extensions has been taken by another extractor.
|
||||
pub fn extensions_mut(&mut self) -> Option<&mut Extensions> {
|
||||
self.extensions.as_mut()
|
||||
}
|
||||
|
||||
/// Takes the extensions out of the request, leaving a `None` in its place.
|
||||
pub fn take_extensions(&mut self) -> Option<Extensions> {
|
||||
self.extensions.take()
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn body_mut(&mut self) -> Option<&mut B> {
|
||||
self.body.as_mut()
|
||||
}
|
||||
|
||||
/// Takes the body out of the request, leaving a `None` in its place.
|
||||
pub fn take_body(&mut self) -> Option<B> {
|
||||
self.body.take()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -223,7 +412,7 @@ where
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
|
||||
async fn from_request(req: &mut Request<B>) -> Result<Option<T>, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Option<T>, Self::Rejection> {
|
||||
Ok(T::from_request(req).await.ok())
|
||||
}
|
||||
}
|
||||
@@ -236,7 +425,7 @@ where
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
|
||||
async fn from_request(req: &mut Request<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
Ok(T::from_request(req).await)
|
||||
}
|
||||
}
|
||||
@@ -284,8 +473,12 @@ where
|
||||
{
|
||||
type Rejection = QueryRejection;
|
||||
|
||||
async fn from_request(req: &mut Request<B>) -> Result<Self, Self::Rejection> {
|
||||
let query = req.uri().query().ok_or(QueryStringMissing)?;
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
let query = req
|
||||
.uri()
|
||||
.ok_or(UriAlreadyExtracted)?
|
||||
.query()
|
||||
.ok_or(QueryStringMissing)?;
|
||||
let value = serde_urlencoded::from_str(query)
|
||||
.map_err(FailedToDeserializeQueryString::new::<T, _>)?;
|
||||
Ok(Query(value))
|
||||
@@ -329,20 +522,24 @@ pub struct Form<T>(pub T);
|
||||
impl<T, B> FromRequest<B> for Form<T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
B: http_body::Body + Default + Send,
|
||||
B: http_body::Body + Send,
|
||||
B::Data: Send,
|
||||
B::Error: Into<tower::BoxError>,
|
||||
{
|
||||
type Rejection = FormRejection;
|
||||
|
||||
#[allow(warnings)]
|
||||
async fn from_request(req: &mut Request<B>) -> Result<Self, Self::Rejection> {
|
||||
if !has_content_type(&req, "application/x-www-form-urlencoded") {
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
if !has_content_type(&req, "application/x-www-form-urlencoded")? {
|
||||
Err(InvalidFormContentType)?;
|
||||
}
|
||||
|
||||
if req.method() == Method::GET {
|
||||
let query = req.uri().query().ok_or(QueryStringMissing)?;
|
||||
if req.method().ok_or(MethodAlreadyExtracted)? == Method::GET {
|
||||
let query = req
|
||||
.uri()
|
||||
.ok_or(UriAlreadyExtracted)?
|
||||
.query()
|
||||
.ok_or(QueryStringMissing)?;
|
||||
let value = serde_urlencoded::from_str(query)
|
||||
.map_err(FailedToDeserializeQueryString::new::<T, _>)?;
|
||||
Ok(Form(value))
|
||||
@@ -398,16 +595,16 @@ pub struct Json<T>(pub T);
|
||||
impl<T, B> FromRequest<B> for Json<T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
B: http_body::Body + Default + Send,
|
||||
B: http_body::Body + Send,
|
||||
B::Data: Send,
|
||||
B::Error: Into<tower::BoxError>,
|
||||
{
|
||||
type Rejection = JsonRejection;
|
||||
|
||||
async fn from_request(req: &mut Request<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
use bytes::Buf;
|
||||
|
||||
if has_content_type(req, "application/json") {
|
||||
if has_content_type(req, "application/json")? {
|
||||
let body = take_body(req)?;
|
||||
|
||||
let buf = hyper::body::aggregate(body)
|
||||
@@ -423,20 +620,27 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn has_content_type<B>(req: &Request<B>, expected_content_type: &str) -> bool {
|
||||
let content_type = if let Some(content_type) = req.headers().get(header::CONTENT_TYPE) {
|
||||
fn has_content_type<B>(
|
||||
req: &RequestParts<B>,
|
||||
expected_content_type: &str,
|
||||
) -> Result<bool, HeadersAlreadyExtracted> {
|
||||
let content_type = if let Some(content_type) = req
|
||||
.headers()
|
||||
.ok_or(HeadersAlreadyExtracted)?
|
||||
.get(header::CONTENT_TYPE)
|
||||
{
|
||||
content_type
|
||||
} else {
|
||||
return false;
|
||||
return Ok(false);
|
||||
};
|
||||
|
||||
let content_type = if let Ok(content_type) = content_type.to_str() {
|
||||
content_type
|
||||
} else {
|
||||
return false;
|
||||
return Ok(false);
|
||||
};
|
||||
|
||||
content_type.starts_with(expected_content_type)
|
||||
Ok(content_type.starts_with(expected_content_type))
|
||||
}
|
||||
|
||||
/// Extractor that gets a value from request extensions.
|
||||
@@ -480,11 +684,12 @@ where
|
||||
T: Clone + Send + Sync + 'static,
|
||||
B: Send,
|
||||
{
|
||||
type Rejection = MissingExtension;
|
||||
type Rejection = ExtensionRejection;
|
||||
|
||||
async fn from_request(req: &mut Request<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
let value = req
|
||||
.extensions()
|
||||
.ok_or(ExtensionsAlreadyExtracted)?
|
||||
.get::<T>()
|
||||
.ok_or(MissingExtension)
|
||||
.map(|x| x.clone())?;
|
||||
@@ -496,13 +701,13 @@ where
|
||||
#[async_trait]
|
||||
impl<B> FromRequest<B> for Bytes
|
||||
where
|
||||
B: http_body::Body + Default + Send,
|
||||
B: http_body::Body + Send,
|
||||
B::Data: Send,
|
||||
B::Error: Into<tower::BoxError>,
|
||||
{
|
||||
type Rejection = BytesRejection;
|
||||
|
||||
async fn from_request(req: &mut Request<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
let body = take_body(req)?;
|
||||
|
||||
let bytes = hyper::body::to_bytes(body)
|
||||
@@ -516,13 +721,13 @@ where
|
||||
#[async_trait]
|
||||
impl<B> FromRequest<B> for String
|
||||
where
|
||||
B: http_body::Body + Default + Send,
|
||||
B: http_body::Body + Send,
|
||||
B::Data: Send,
|
||||
B::Error: Into<tower::BoxError>,
|
||||
{
|
||||
type Rejection = StringRejection;
|
||||
|
||||
async fn from_request(req: &mut Request<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
let body = take_body(req)?;
|
||||
|
||||
let bytes = hyper::body::to_bytes(body)
|
||||
@@ -572,11 +777,11 @@ where
|
||||
#[async_trait]
|
||||
impl<B> FromRequest<B> for BodyStream<B>
|
||||
where
|
||||
B: http_body::Body + Default + Unpin + Send,
|
||||
B: http_body::Body + Unpin + Send,
|
||||
{
|
||||
type Rejection = BodyAlreadyExtracted;
|
||||
|
||||
async fn from_request(req: &mut Request<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
let body = take_body(req)?;
|
||||
let stream = BodyStream(body);
|
||||
Ok(stream)
|
||||
@@ -586,21 +791,22 @@ where
|
||||
#[async_trait]
|
||||
impl<B> FromRequest<B> for Request<B>
|
||||
where
|
||||
B: Default + Send,
|
||||
B: Send,
|
||||
{
|
||||
type Rejection = RequestAlreadyExtracted;
|
||||
|
||||
async fn from_request(req: &mut Request<B>) -> Result<Self, Self::Rejection> {
|
||||
struct RequestAlreadyExtractedExt;
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
let all_parts = req
|
||||
.method()
|
||||
.zip(req.uri())
|
||||
.zip(req.headers())
|
||||
.zip(req.extensions())
|
||||
.zip(req.body());
|
||||
|
||||
if req
|
||||
.extensions_mut()
|
||||
.insert(RequestAlreadyExtractedExt)
|
||||
.is_some()
|
||||
{
|
||||
Err(RequestAlreadyExtracted)
|
||||
if all_parts.is_some() {
|
||||
Ok(req.into_request())
|
||||
} else {
|
||||
Ok(mem::take(req))
|
||||
Err(RequestAlreadyExtracted)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -610,10 +816,10 @@ impl<B> FromRequest<B> for Method
|
||||
where
|
||||
B: Send,
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
type Rejection = MethodAlreadyExtracted;
|
||||
|
||||
async fn from_request(req: &mut Request<B>) -> Result<Self, Self::Rejection> {
|
||||
Ok(req.method().clone())
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
req.take_method().ok_or(MethodAlreadyExtracted)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -622,10 +828,10 @@ impl<B> FromRequest<B> for Uri
|
||||
where
|
||||
B: Send,
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
type Rejection = UriAlreadyExtracted;
|
||||
|
||||
async fn from_request(req: &mut Request<B>) -> Result<Self, Self::Rejection> {
|
||||
Ok(req.uri().clone())
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
req.take_uri().ok_or(UriAlreadyExtracted)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -634,10 +840,10 @@ impl<B> FromRequest<B> for Version
|
||||
where
|
||||
B: Send,
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
type Rejection = VersionAlreadyExtracted;
|
||||
|
||||
async fn from_request(req: &mut Request<B>) -> Result<Self, Self::Rejection> {
|
||||
Ok(req.version())
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
req.take_version().ok_or(VersionAlreadyExtracted)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -646,10 +852,10 @@ impl<B> FromRequest<B> for HeaderMap
|
||||
where
|
||||
B: Send,
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
type Rejection = HeadersAlreadyExtracted;
|
||||
|
||||
async fn from_request(req: &mut Request<B>) -> Result<Self, Self::Rejection> {
|
||||
Ok(mem::take(req.headers_mut()))
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
req.take_headers().ok_or(HeadersAlreadyExtracted)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -682,8 +888,13 @@ where
|
||||
{
|
||||
type Rejection = ContentLengthLimitRejection<T::Rejection>;
|
||||
|
||||
async fn from_request(req: &mut Request<B>) -> Result<Self, Self::Rejection> {
|
||||
let content_length = req.headers().get(http::header::CONTENT_LENGTH).cloned();
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
let content_length = req
|
||||
.headers()
|
||||
.ok_or(ContentLengthLimitRejection::HeadersAlreadyExtracted(
|
||||
HeadersAlreadyExtracted,
|
||||
))?
|
||||
.get(http::header::CONTENT_LENGTH);
|
||||
|
||||
let content_length =
|
||||
content_length.and_then(|value| value.to_str().ok()?.parse::<u64>().ok());
|
||||
@@ -752,10 +963,10 @@ where
|
||||
{
|
||||
type Rejection = MissingRouteParams;
|
||||
|
||||
async fn from_request(req: &mut Request<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
if let Some(params) = req
|
||||
.extensions_mut()
|
||||
.get_mut::<Option<crate::routing::UrlParams>>()
|
||||
.and_then(|ext| ext.get_mut::<Option<crate::routing::UrlParams>>())
|
||||
{
|
||||
if let Some(params) = params {
|
||||
Ok(Self(params.0.iter().cloned().collect()))
|
||||
@@ -810,10 +1021,12 @@ macro_rules! impl_parse_url {
|
||||
type Rejection = UrlParamsRejection;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
async fn from_request(req: &mut Request<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
let params = if let Some(params) = req
|
||||
.extensions_mut()
|
||||
.get_mut::<Option<crate::routing::UrlParams>>()
|
||||
.and_then(|ext| {
|
||||
ext.get_mut::<Option<crate::routing::UrlParams>>()
|
||||
})
|
||||
{
|
||||
if let Some(params) = params {
|
||||
params.0.clone()
|
||||
@@ -852,23 +1065,8 @@ macro_rules! impl_parse_url {
|
||||
|
||||
impl_parse_url!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16);
|
||||
|
||||
/// Request extension used to indicate that body has been extracted and `Default` has been left in
|
||||
/// its place.
|
||||
struct BodyAlreadyExtractedExt;
|
||||
|
||||
fn take_body<B>(req: &mut Request<B>) -> Result<B, BodyAlreadyExtracted>
|
||||
where
|
||||
B: Default,
|
||||
{
|
||||
if req
|
||||
.extensions_mut()
|
||||
.insert(BodyAlreadyExtractedExt)
|
||||
.is_some()
|
||||
{
|
||||
Err(BodyAlreadyExtracted)
|
||||
} else {
|
||||
Ok(mem::take(req.body_mut()))
|
||||
}
|
||||
fn take_body<B>(req: &mut RequestParts<B>) -> Result<B, BodyAlreadyExtracted> {
|
||||
req.take_body().ok_or(BodyAlreadyExtracted)
|
||||
}
|
||||
|
||||
/// Extractor that extracts a typed header value from [`headers`].
|
||||
@@ -903,10 +1101,16 @@ where
|
||||
T: headers::Header,
|
||||
B: Send,
|
||||
{
|
||||
type Rejection = rejection::TypedHeaderRejection;
|
||||
type Rejection = TypedHeaderRejection;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
let empty_headers = HeaderMap::new();
|
||||
let header_values = if let Some(headers) = req.headers() {
|
||||
headers.get_all(T::name())
|
||||
} else {
|
||||
empty_headers.get_all(T::name())
|
||||
};
|
||||
|
||||
async fn from_request(req: &mut Request<B>) -> Result<Self, Self::Rejection> {
|
||||
let header_values = req.headers().get_all(T::name());
|
||||
T::decode(&mut header_values.iter())
|
||||
.map(Self)
|
||||
.map_err(|err| rejection::TypedHeaderRejection {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//!
|
||||
//! See [`Multipart`] for more details.
|
||||
|
||||
use super::{rejection::*, BodyStream, FromRequest};
|
||||
use super::{rejection::*, BodyStream, FromRequest, RequestParts};
|
||||
use async_trait::async_trait;
|
||||
use bytes::Bytes;
|
||||
use futures_util::stream::Stream;
|
||||
@@ -53,9 +53,10 @@ where
|
||||
{
|
||||
type Rejection = MultipartRejection;
|
||||
|
||||
async fn from_request(req: &mut http::Request<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
let stream = BodyStream::from_request(req).await?;
|
||||
let boundary = parse_boundary(req.headers()).ok_or(InvalidBoundary)?;
|
||||
let headers = req.headers().ok_or(HeadersAlreadyExtracted)?;
|
||||
let boundary = parse_boundary(headers).ok_or(InvalidBoundary)?;
|
||||
let multipart = multer::Multipart::new(stream, boundary);
|
||||
Ok(Self { inner: multipart })
|
||||
}
|
||||
@@ -175,6 +176,7 @@ composite_rejection! {
|
||||
pub enum MultipartRejection {
|
||||
BodyAlreadyExtracted,
|
||||
InvalidBoundary,
|
||||
HeadersAlreadyExtracted,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,41 @@ use super::IntoResponse;
|
||||
use crate::body::Body;
|
||||
use tower::BoxError;
|
||||
|
||||
define_rejection! {
|
||||
#[status = INTERNAL_SERVER_ERROR]
|
||||
#[body = "Version taken by other extractor"]
|
||||
/// Rejection used if the HTTP version has been taken by another extractor.
|
||||
pub struct VersionAlreadyExtracted;
|
||||
}
|
||||
|
||||
define_rejection! {
|
||||
#[status = INTERNAL_SERVER_ERROR]
|
||||
#[body = "URI taken by other extractor"]
|
||||
/// Rejection used if the URI has been taken by another extractor.
|
||||
pub struct UriAlreadyExtracted;
|
||||
}
|
||||
|
||||
define_rejection! {
|
||||
#[status = INTERNAL_SERVER_ERROR]
|
||||
#[body = "Method taken by other extractor"]
|
||||
/// Rejection used if the method has been taken by another extractor.
|
||||
pub struct MethodAlreadyExtracted;
|
||||
}
|
||||
|
||||
define_rejection! {
|
||||
#[status = INTERNAL_SERVER_ERROR]
|
||||
#[body = "Extensions taken by other extractor"]
|
||||
/// Rejection used if the method has been taken by another extractor.
|
||||
pub struct ExtensionsAlreadyExtracted;
|
||||
}
|
||||
|
||||
define_rejection! {
|
||||
#[status = INTERNAL_SERVER_ERROR]
|
||||
#[body = "Headers taken by other extractor"]
|
||||
/// Rejection used if the URI has been taken by another extractor.
|
||||
pub struct HeadersAlreadyExtracted;
|
||||
}
|
||||
|
||||
define_rejection! {
|
||||
#[status = BAD_REQUEST]
|
||||
#[body = "Query string was invalid or missing"]
|
||||
@@ -160,6 +195,7 @@ composite_rejection! {
|
||||
/// Contains one variant for each way the [`Query`](super::Query) extractor
|
||||
/// can fail.
|
||||
pub enum QueryRejection {
|
||||
UriAlreadyExtracted,
|
||||
QueryStringMissing,
|
||||
FailedToDeserializeQueryString,
|
||||
}
|
||||
@@ -176,6 +212,9 @@ composite_rejection! {
|
||||
FailedToDeserializeQueryString,
|
||||
FailedToBufferBody,
|
||||
BodyAlreadyExtracted,
|
||||
UriAlreadyExtracted,
|
||||
HeadersAlreadyExtracted,
|
||||
MethodAlreadyExtracted,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,6 +227,18 @@ composite_rejection! {
|
||||
InvalidJsonBody,
|
||||
MissingJsonContentType,
|
||||
BodyAlreadyExtracted,
|
||||
HeadersAlreadyExtracted,
|
||||
}
|
||||
}
|
||||
|
||||
composite_rejection! {
|
||||
/// Rejection used for [`Extension`](super::Extension).
|
||||
///
|
||||
/// Contains one variant for each way the [`Extension`](super::Extension) extractor
|
||||
/// can fail.
|
||||
pub enum ExtensionRejection {
|
||||
MissingExtension,
|
||||
ExtensionsAlreadyExtracted,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,6 +287,8 @@ pub enum ContentLengthLimitRejection<T> {
|
||||
#[allow(missing_docs)]
|
||||
LengthRequired(LengthRequired),
|
||||
#[allow(missing_docs)]
|
||||
HeadersAlreadyExtracted(HeadersAlreadyExtracted),
|
||||
#[allow(missing_docs)]
|
||||
Inner(T),
|
||||
}
|
||||
|
||||
@@ -247,6 +300,7 @@ where
|
||||
match self {
|
||||
Self::PayloadTooLarge(inner) => inner.into_response(),
|
||||
Self::LengthRequired(inner) => inner.into_response(),
|
||||
Self::HeadersAlreadyExtracted(inner) => inner.into_response(),
|
||||
Self::Inner(inner) => inner.into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user