mirror of
https://github.com/tokio-rs/axum.git
synced 2026-09-06 00:00:17 +02:00
Move FromRequest and IntoResponse into new axum-core crate (#564)
* Move `IntoResponse` to axum-core * Move `FromRequest` to axum-core * some clean up * Remove hyper dependency from axum-core * Fix docs reference * Use default * Update changelog * Remove mention of default type
This commit is contained in:
@@ -0,0 +1,284 @@
|
||||
//! Types and traits for extracting data from requests.
|
||||
//!
|
||||
//! See [`axum::extract`] for more details.
|
||||
//!
|
||||
//! [`axum::extract`]: https://docs.rs/axum/latest/axum/extract/index.html
|
||||
|
||||
use self::rejection::*;
|
||||
use crate::response::IntoResponse;
|
||||
use crate::Error;
|
||||
use async_trait::async_trait;
|
||||
use http::{Extensions, HeaderMap, Method, Request, Uri, Version};
|
||||
use std::convert::Infallible;
|
||||
|
||||
pub mod rejection;
|
||||
|
||||
mod request_parts;
|
||||
mod tuple;
|
||||
|
||||
/// Types that can be created from requests.
|
||||
///
|
||||
/// See [`axum::extract`] for more details.
|
||||
///
|
||||
/// # What is the `B` type parameter?
|
||||
///
|
||||
/// `FromRequest` is generic over the request body (the `B` in
|
||||
/// [`http::Request<B>`]). This is to allow `FromRequest` to be usable with any
|
||||
/// type of request body. This is necessary because some middleware change the
|
||||
/// request body, for example to add timeouts.
|
||||
///
|
||||
/// If you're writing your own `FromRequest` that wont be used outside your
|
||||
/// application, and not using any middleware that changes the request body, you
|
||||
/// can most likely use `axum::body::Body`.
|
||||
///
|
||||
/// If you're writing a library that's intended for others to use, it's recommended
|
||||
/// to keep the generic type parameter:
|
||||
///
|
||||
/// ```rust
|
||||
/// use axum::{
|
||||
/// async_trait,
|
||||
/// extract::{FromRequest, RequestParts},
|
||||
/// };
|
||||
///
|
||||
/// struct MyExtractor;
|
||||
///
|
||||
/// #[async_trait]
|
||||
/// impl<B> FromRequest<B> for MyExtractor
|
||||
/// where
|
||||
/// B: Send, // required by `async_trait`
|
||||
/// {
|
||||
/// type Rejection = http::StatusCode;
|
||||
///
|
||||
/// async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
/// // ...
|
||||
/// # unimplemented!()
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// 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
|
||||
#[async_trait]
|
||||
pub trait FromRequest<B>: 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<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: Method,
|
||||
uri: Uri,
|
||||
version: Version,
|
||||
headers: Option<HeaderMap>,
|
||||
extensions: Option<Extensions>,
|
||||
body: Option<B>,
|
||||
}
|
||||
|
||||
impl<B> RequestParts<B> {
|
||||
/// Create a new `RequestParts`.
|
||||
///
|
||||
/// 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 {
|
||||
let (
|
||||
http::request::Parts {
|
||||
method,
|
||||
uri,
|
||||
version,
|
||||
headers,
|
||||
extensions,
|
||||
..
|
||||
},
|
||||
body,
|
||||
) = req.into_parts();
|
||||
|
||||
RequestParts {
|
||||
method,
|
||||
uri,
|
||||
version,
|
||||
headers: Some(headers),
|
||||
extensions: Some(extensions),
|
||||
body: Some(body),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert this `RequestParts` back into a [`Request`].
|
||||
///
|
||||
/// Fails if
|
||||
///
|
||||
/// - The full [`HeaderMap`] has been extracted, that is [`take_headers`]
|
||||
/// have been called.
|
||||
/// - The full [`Extensions`] has been extracted, that is
|
||||
/// [`take_extensions`] have been called.
|
||||
/// - The request body has been extracted, that is [`take_body`] have been
|
||||
/// called.
|
||||
///
|
||||
/// [`take_headers`]: RequestParts::take_headers
|
||||
/// [`take_extensions`]: RequestParts::take_extensions
|
||||
/// [`take_body`]: RequestParts::take_body
|
||||
pub fn try_into_request(self) -> Result<Request<B>, Error> {
|
||||
let Self {
|
||||
method,
|
||||
uri,
|
||||
version,
|
||||
mut headers,
|
||||
mut extensions,
|
||||
mut body,
|
||||
} = self;
|
||||
|
||||
let mut req = if let Some(body) = body.take() {
|
||||
Request::new(body)
|
||||
} else {
|
||||
return Err(Error::new(RequestAlreadyExtracted::BodyAlreadyExtracted(
|
||||
BodyAlreadyExtracted,
|
||||
)));
|
||||
};
|
||||
|
||||
*req.method_mut() = method;
|
||||
*req.uri_mut() = uri;
|
||||
*req.version_mut() = version;
|
||||
|
||||
if let Some(headers) = headers.take() {
|
||||
*req.headers_mut() = headers;
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
RequestAlreadyExtracted::HeadersAlreadyExtracted(HeadersAlreadyExtracted),
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(extensions) = extensions.take() {
|
||||
*req.extensions_mut() = extensions;
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
RequestAlreadyExtracted::ExtensionsAlreadyExtracted(ExtensionsAlreadyExtracted),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(req)
|
||||
}
|
||||
|
||||
/// Gets a reference 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 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.
|
||||
///
|
||||
/// 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]
|
||||
impl<T, B> FromRequest<B> for Option<T>
|
||||
where
|
||||
T: FromRequest<B>,
|
||||
B: Send,
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Option<T>, Self::Rejection> {
|
||||
Ok(T::from_request(req).await.ok())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T, B> FromRequest<B> for Result<T, T::Rejection>
|
||||
where
|
||||
T: FromRequest<B>,
|
||||
B: Send,
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
Ok(T::from_request(req).await)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
//! Rejection response types.
|
||||
|
||||
define_rejection! {
|
||||
#[status = INTERNAL_SERVER_ERROR]
|
||||
#[body = "Cannot have two request body extractors for a single handler"]
|
||||
/// Rejection type used if you try and extract the request body more than
|
||||
/// once.
|
||||
pub struct BodyAlreadyExtracted;
|
||||
}
|
||||
|
||||
define_rejection! {
|
||||
#[status = INTERNAL_SERVER_ERROR]
|
||||
#[body = "Headers taken by other extractor"]
|
||||
/// Rejection used if the headers has been taken by another extractor.
|
||||
pub struct HeadersAlreadyExtracted;
|
||||
}
|
||||
|
||||
define_rejection! {
|
||||
#[status = INTERNAL_SERVER_ERROR]
|
||||
#[body = "Extensions taken by other extractor"]
|
||||
/// Rejection used if the request extension has been taken by another
|
||||
/// extractor.
|
||||
pub struct ExtensionsAlreadyExtracted;
|
||||
}
|
||||
|
||||
define_rejection! {
|
||||
#[status = BAD_REQUEST]
|
||||
#[body = "Failed to buffer the request body"]
|
||||
/// Rejection type for extractors that buffer the request body. Used if the
|
||||
/// request body cannot be buffered due to an error.
|
||||
pub struct FailedToBufferBody(Error);
|
||||
}
|
||||
|
||||
define_rejection! {
|
||||
#[status = BAD_REQUEST]
|
||||
#[body = "Request body didn't contain valid UTF-8"]
|
||||
/// Rejection type used when buffering the request into a [`String`] if the
|
||||
/// body doesn't contain valid UTF-8.
|
||||
pub struct InvalidUtf8(Error);
|
||||
}
|
||||
|
||||
composite_rejection! {
|
||||
/// Rejection used for [`Request<_>`].
|
||||
///
|
||||
/// Contains one variant for each way the [`Request<_>`] extractor can fail.
|
||||
///
|
||||
/// [`Request<_>`]: http::Request
|
||||
pub enum RequestAlreadyExtracted {
|
||||
BodyAlreadyExtracted,
|
||||
HeadersAlreadyExtracted,
|
||||
ExtensionsAlreadyExtracted,
|
||||
}
|
||||
}
|
||||
|
||||
composite_rejection! {
|
||||
/// Rejection used for [`Bytes`](bytes::Bytes).
|
||||
///
|
||||
/// Contains one variant for each way the [`Bytes`](bytes::Bytes) extractor
|
||||
/// can fail.
|
||||
pub enum BytesRejection {
|
||||
BodyAlreadyExtracted,
|
||||
FailedToBufferBody,
|
||||
}
|
||||
}
|
||||
|
||||
composite_rejection! {
|
||||
/// Rejection used for [`String`].
|
||||
///
|
||||
/// Contains one variant for each way the [`String`] extractor can fail.
|
||||
pub enum StringRejection {
|
||||
BodyAlreadyExtracted,
|
||||
FailedToBufferBody,
|
||||
InvalidUtf8,
|
||||
}
|
||||
}
|
||||
|
||||
composite_rejection! {
|
||||
/// Rejection used for [`http::request::Parts`].
|
||||
///
|
||||
/// Contains one variant for each way the [`http::request::Parts`] extractor can fail.
|
||||
pub enum RequestPartsAlreadyExtracted {
|
||||
HeadersAlreadyExtracted,
|
||||
ExtensionsAlreadyExtracted,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
use super::{rejection::*, FromRequest, RequestParts};
|
||||
use crate::BoxError;
|
||||
use async_trait::async_trait;
|
||||
use bytes::Bytes;
|
||||
use http::{Extensions, HeaderMap, Method, Request, Uri, Version};
|
||||
use std::convert::Infallible;
|
||||
|
||||
#[async_trait]
|
||||
impl<B> FromRequest<B> for Request<B>
|
||||
where
|
||||
B: Send,
|
||||
{
|
||||
type Rejection = RequestAlreadyExtracted;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
let req = std::mem::replace(
|
||||
req,
|
||||
RequestParts {
|
||||
method: req.method.clone(),
|
||||
version: req.version,
|
||||
uri: req.uri.clone(),
|
||||
headers: None,
|
||||
extensions: None,
|
||||
body: None,
|
||||
},
|
||||
);
|
||||
|
||||
let err = match req.try_into_request() {
|
||||
Ok(req) => return Ok(req),
|
||||
Err(err) => err,
|
||||
};
|
||||
|
||||
match err.downcast::<RequestAlreadyExtracted>() {
|
||||
Ok(err) => return Err(err),
|
||||
Err(err) => unreachable!(
|
||||
"Unexpected error type from `try_into_request`: `{:?}`. This is a bug in axum, please file an issue",
|
||||
err,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<B> FromRequest<B> for Method
|
||||
where
|
||||
B: Send,
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
Ok(req.method().clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<B> FromRequest<B> for Uri
|
||||
where
|
||||
B: Send,
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
Ok(req.uri().clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<B> FromRequest<B> for Version
|
||||
where
|
||||
B: Send,
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
Ok(req.version())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<B> FromRequest<B> for HeaderMap
|
||||
where
|
||||
B: Send,
|
||||
{
|
||||
type Rejection = HeadersAlreadyExtracted;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
req.take_headers().ok_or(HeadersAlreadyExtracted)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<B> FromRequest<B> for Extensions
|
||||
where
|
||||
B: Send,
|
||||
{
|
||||
type Rejection = ExtensionsAlreadyExtracted;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
req.take_extensions().ok_or(ExtensionsAlreadyExtracted)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<B> FromRequest<B> for Bytes
|
||||
where
|
||||
B: http_body::Body + Send,
|
||||
B::Data: Send,
|
||||
B::Error: Into<BoxError>,
|
||||
{
|
||||
type Rejection = BytesRejection;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
let body = take_body(req)?;
|
||||
|
||||
let bytes = crate::body::to_bytes(body)
|
||||
.await
|
||||
.map_err(FailedToBufferBody::from_err)?;
|
||||
|
||||
Ok(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<B> FromRequest<B> for String
|
||||
where
|
||||
B: http_body::Body + Send,
|
||||
B::Data: Send,
|
||||
B::Error: Into<BoxError>,
|
||||
{
|
||||
type Rejection = StringRejection;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
let body = take_body(req)?;
|
||||
|
||||
let bytes = crate::body::to_bytes(body)
|
||||
.await
|
||||
.map_err(FailedToBufferBody::from_err)?
|
||||
.to_vec();
|
||||
|
||||
let string = String::from_utf8(bytes).map_err(InvalidUtf8::from_err)?;
|
||||
|
||||
Ok(string)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<B> FromRequest<B> for http::request::Parts
|
||||
where
|
||||
B: Send,
|
||||
{
|
||||
type Rejection = RequestPartsAlreadyExtracted;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<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 = HeaderMap::from_request(req).await?;
|
||||
let extensions = Extensions::from_request(req).await?;
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
fn unwrap_infallible<T>(result: Result<T, Infallible>) -> T {
|
||||
match result {
|
||||
Ok(value) => value,
|
||||
Err(err) => match err {},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn take_body<B>(req: &mut RequestParts<B>) -> Result<B, BodyAlreadyExtracted> {
|
||||
req.take_body().ok_or(BodyAlreadyExtracted)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
use super::{FromRequest, RequestParts};
|
||||
use crate::{body::BoxBody, response::IntoResponse};
|
||||
use async_trait::async_trait;
|
||||
use http::Response;
|
||||
use std::convert::Infallible;
|
||||
|
||||
#[async_trait]
|
||||
impl<B> FromRequest<B> for ()
|
||||
where
|
||||
B: Send,
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
|
||||
async fn from_request(_: &mut RequestParts<B>) -> Result<(), Self::Rejection> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_from_request {
|
||||
() => {};
|
||||
|
||||
( $($ty:ident),* $(,)? ) => {
|
||||
#[async_trait]
|
||||
#[allow(non_snake_case)]
|
||||
impl<B, $($ty,)*> FromRequest<B> for ($($ty,)*)
|
||||
where
|
||||
$( $ty: FromRequest<B> + Send, )*
|
||||
B: Send,
|
||||
{
|
||||
type Rejection = Response<BoxBody>;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
$( let $ty = $ty::from_request(req).await.map_err(|err| err.into_response())?; )*
|
||||
Ok(($($ty,)*))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
all_the_tuples!(impl_from_request);
|
||||
Reference in New Issue
Block a user