Remove B type param (#1751)

Co-authored-by: Jonas Platte <jplatte+git@posteo.de>
Co-authored-by: Michael Scofield <mscofield0@tutanota.com>
This commit is contained in:
David Pedersen 2023-03-12 16:37:32 +01:00
parent 9be0ea934c
commit 4e4c29175f
100 changed files with 966 additions and 1160 deletions

View file

@ -1,15 +1,16 @@
use crate::body::Body;
use crate::extract::{DefaultBodyLimitKind, FromRequest, FromRequestParts};
use futures_util::future::BoxFuture;
use http::Request;
use http_body::Limited;
mod sealed {
pub trait Sealed<B> {}
impl<B> Sealed<B> for http::Request<B> {}
pub trait Sealed {}
impl Sealed for http::Request<crate::body::Body> {}
}
/// Extension trait that adds additional methods to [`Request`].
pub trait RequestExt<B>: sealed::Sealed<B> + Sized {
pub trait RequestExt: sealed::Sealed + Sized {
/// Apply an extractor to this `Request`.
///
/// This is just a convenience for `E::from_request(req, &())`.
@ -23,6 +24,7 @@ pub trait RequestExt<B>: sealed::Sealed<B> + Sized {
/// use axum::{
/// async_trait,
/// extract::FromRequest,
/// body::Body,
/// http::{header::CONTENT_TYPE, Request, StatusCode},
/// response::{IntoResponse, Response},
/// Form, Json, RequestExt,
@ -31,17 +33,16 @@ pub trait RequestExt<B>: sealed::Sealed<B> + Sized {
/// struct FormOrJson<T>(T);
///
/// #[async_trait]
/// impl<S, B, T> FromRequest<S, B> for FormOrJson<T>
/// impl<S, T> FromRequest<S> for FormOrJson<T>
/// where
/// Json<T>: FromRequest<(), B>,
/// Form<T>: FromRequest<(), B>,
/// Json<T>: FromRequest<()>,
/// Form<T>: FromRequest<()>,
/// T: 'static,
/// B: Send + 'static,
/// S: Send + Sync,
/// {
/// type Rejection = Response;
///
/// async fn from_request(req: Request<B>, _state: &S) -> Result<Self, Self::Rejection> {
/// async fn from_request(req: Request<Body>, _state: &S) -> Result<Self, Self::Rejection> {
/// let content_type = req
/// .headers()
/// .get(CONTENT_TYPE)
@ -70,7 +71,7 @@ pub trait RequestExt<B>: sealed::Sealed<B> + Sized {
/// ```
fn extract<E, M>(self) -> BoxFuture<'static, Result<E, E::Rejection>>
where
E: FromRequest<(), B, M> + 'static,
E: FromRequest<(), M> + 'static,
M: 'static;
/// Apply an extractor that requires some state to this `Request`.
@ -85,6 +86,7 @@ pub trait RequestExt<B>: sealed::Sealed<B> + Sized {
/// ```
/// use axum::{
/// async_trait,
/// body::Body,
/// extract::{FromRef, FromRequest},
/// http::Request,
/// RequestExt,
@ -95,15 +97,14 @@ pub trait RequestExt<B>: sealed::Sealed<B> + Sized {
/// }
///
/// #[async_trait]
/// impl<S, B> FromRequest<S, B> for MyExtractor
/// impl<S> FromRequest<S> for MyExtractor
/// where
/// String: FromRef<S>,
/// S: Send + Sync,
/// B: Send + 'static,
/// {
/// type Rejection = std::convert::Infallible;
///
/// async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
/// async fn from_request(req: Request<Body>, state: &S) -> Result<Self, Self::Rejection> {
/// let requires_state = req.extract_with_state::<RequiresState, _, _>(state).await?;
///
/// Ok(Self { requires_state })
@ -114,22 +115,21 @@ pub trait RequestExt<B>: sealed::Sealed<B> + Sized {
/// struct RequiresState { /* ... */ }
///
/// #[async_trait]
/// impl<S, B> FromRequest<S, B> for RequiresState
/// impl<S> FromRequest<S> for RequiresState
/// where
/// String: FromRef<S>,
/// S: Send + Sync,
/// B: Send + 'static,
/// {
/// // ...
/// # type Rejection = std::convert::Infallible;
/// # async fn from_request(req: Request<B>, _state: &S) -> Result<Self, Self::Rejection> {
/// # async fn from_request(req: Request<Body>, _state: &S) -> Result<Self, Self::Rejection> {
/// # todo!()
/// # }
/// }
/// ```
fn extract_with_state<E, S, M>(self, state: &S) -> BoxFuture<'_, Result<E, E::Rejection>>
where
E: FromRequest<S, B, M> + 'static,
E: FromRequest<S, M> + 'static,
S: Send + Sync;
/// Apply a parts extractor to this `Request`.
@ -145,6 +145,7 @@ pub trait RequestExt<B>: sealed::Sealed<B> + Sized {
/// headers::{authorization::Bearer, Authorization},
/// http::Request,
/// response::{IntoResponse, Response},
/// body::Body,
/// Json, RequestExt, TypedHeader,
/// };
///
@ -154,16 +155,15 @@ pub trait RequestExt<B>: sealed::Sealed<B> + Sized {
/// }
///
/// #[async_trait]
/// impl<S, B, T> FromRequest<S, B> for MyExtractor<T>
/// impl<S, T> FromRequest<S> for MyExtractor<T>
/// where
/// B: Send + 'static,
/// S: Send + Sync,
/// Json<T>: FromRequest<(), B>,
/// Json<T>: FromRequest<()>,
/// T: 'static,
/// {
/// type Rejection = Response;
///
/// async fn from_request(mut req: Request<B>, _state: &S) -> Result<Self, Self::Rejection> {
/// async fn from_request(mut req: Request<Body>, _state: &S) -> Result<Self, Self::Rejection> {
/// let TypedHeader(auth_header) = req
/// .extract_parts::<TypedHeader<Authorization<Bearer>>>()
/// .await
@ -197,6 +197,7 @@ pub trait RequestExt<B>: sealed::Sealed<B> + Sized {
/// extract::{FromRef, FromRequest, FromRequestParts},
/// http::{request::Parts, Request},
/// response::{IntoResponse, Response},
/// body::Body,
/// Json, RequestExt,
/// };
///
@ -206,17 +207,16 @@ pub trait RequestExt<B>: sealed::Sealed<B> + Sized {
/// }
///
/// #[async_trait]
/// impl<S, B, T> FromRequest<S, B> for MyExtractor<T>
/// impl<S, T> FromRequest<S> for MyExtractor<T>
/// where
/// String: FromRef<S>,
/// Json<T>: FromRequest<(), B>,
/// Json<T>: FromRequest<()>,
/// T: 'static,
/// S: Send + Sync,
/// B: Send + 'static,
/// {
/// type Rejection = Response;
///
/// async fn from_request(mut req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
/// async fn from_request(mut req: Request<Body>, state: &S) -> Result<Self, Self::Rejection> {
/// let requires_state = req
/// .extract_parts_with_state::<RequiresState, _>(state)
/// .await
@ -260,21 +260,18 @@ pub trait RequestExt<B>: sealed::Sealed<B> + Sized {
/// Apply the [default body limit](crate::extract::DefaultBodyLimit).
///
/// If it is disabled, return the request as-is in `Err`.
fn with_limited_body(self) -> Result<Request<Limited<B>>, Request<B>>;
fn with_limited_body(self) -> Result<Request<Limited<Body>>, Request<Body>>;
/// Consumes the request, returning the body wrapped in [`Limited`] if a
/// [default limit](crate::extract::DefaultBodyLimit) is in place, or not wrapped if the
/// default limit is disabled.
fn into_limited_body(self) -> Result<Limited<B>, B>;
fn into_limited_body(self) -> Result<Limited<Body>, Body>;
}
impl<B> RequestExt<B> for Request<B>
where
B: Send + 'static,
{
impl RequestExt for Request<Body> {
fn extract<E, M>(self) -> BoxFuture<'static, Result<E, E::Rejection>>
where
E: FromRequest<(), B, M> + 'static,
E: FromRequest<(), M> + 'static,
M: 'static,
{
self.extract_with_state(&())
@ -282,7 +279,7 @@ where
fn extract_with_state<E, S, M>(self, state: &S) -> BoxFuture<'_, Result<E, E::Rejection>>
where
E: FromRequest<S, B, M> + 'static,
E: FromRequest<S, M> + 'static,
S: Send + Sync,
{
E::from_request(self, state)
@ -324,7 +321,7 @@ where
})
}
fn with_limited_body(self) -> Result<Request<Limited<B>>, Request<B>> {
fn with_limited_body(self) -> Result<Request<Limited<Body>>, Request<Body>> {
// update docs in `axum-core/src/extract/default_body_limit.rs` and
// `axum/src/docs/extract.md` if this changes
const DEFAULT_LIMIT: usize = 2_097_152; // 2 mb
@ -338,7 +335,7 @@ where
}
}
fn into_limited_body(self) -> Result<Limited<B>, B> {
fn into_limited_body(self) -> Result<Limited<Body>, Body> {
self.with_limited_body()
.map(Request::into_body)
.map_err(Request::into_body)
@ -354,11 +351,10 @@ mod tests {
};
use async_trait::async_trait;
use http::Method;
use hyper::Body;
#[tokio::test]
async fn extract_without_state() {
let req = Request::new(());
let req = Request::new(Body::empty());
let method: Method = req.extract().await.unwrap();
@ -376,7 +372,7 @@ mod tests {
#[tokio::test]
async fn extract_with_state() {
let req = Request::new(());
let req = Request::new(Body::empty());
let state = "state".to_owned();
@ -387,7 +383,10 @@ mod tests {
#[tokio::test]
async fn extract_parts_without_state() {
let mut req = Request::builder().header("x-foo", "foo").body(()).unwrap();
let mut req = Request::builder()
.header("x-foo", "foo")
.body(Body::empty())
.unwrap();
let method: Method = req.extract_parts().await.unwrap();
@ -397,7 +396,10 @@ mod tests {
#[tokio::test]
async fn extract_parts_with_state() {
let mut req = Request::builder().header("x-foo", "foo").body(()).unwrap();
let mut req = Request::builder()
.header("x-foo", "foo")
.body(Body::empty())
.unwrap();
let state = "state".to_owned();
@ -417,15 +419,14 @@ mod tests {
}
#[async_trait]
impl<S, B> FromRequest<S, B> for WorksForCustomExtractor
impl<S> FromRequest<S> for WorksForCustomExtractor
where
S: Send + Sync,
B: Send + 'static,
String: FromRef<S> + FromRequest<(), B>,
String: FromRef<S> + FromRequest<()>,
{
type Rejection = <String as FromRequest<(), B>>::Rejection;
type Rejection = <String as FromRequest<()>>::Rejection;
async fn from_request(mut req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(mut req: Request<Body>, state: &S) -> Result<Self, Self::Rejection> {
let RequiresState(from_state) = req.extract_parts_with_state(state).await.unwrap();
let method = req.extract_parts().await.unwrap();
let body = req.extract().await?;

View file

@ -41,7 +41,7 @@ use tower_layer::Layer;
/// post(|request: Request<Body>| async {}),
/// )
/// .layer(DefaultBodyLimit::max(1024));
/// # let _: Router<(), _> = app;
/// # let _: Router = app;
/// ```
///
/// ```
@ -54,10 +54,10 @@ use tower_layer::Layer;
/// "/",
/// // `RequestBodyLimitLayer` changes the request body type to `Limited<Body>`
/// // extracting a different body type wont work
/// post(|request: Request<Limited<Body>>| async {}),
/// post(|request: Request<Body>| async {}),
/// )
/// .layer(RequestBodyLimitLayer::new(1024));
/// # let _: Router<(), _> = app;
/// # let _: Router = app;
/// ```
///
/// In general using `DefaultBodyLimit` is recommended but if you need to use third party
@ -105,7 +105,7 @@ impl DefaultBodyLimit {
/// use tower_http::limit::RequestBodyLimitLayer;
/// use http_body::Limited;
///
/// let app: Router<(), Limited<Body>> = Router::new()
/// let app: Router<()> = Router::new()
/// .route("/", get(|body: Bytes| async {}))
/// // Disable the default limit
/// .layer(DefaultBodyLimit::disable())
@ -140,7 +140,7 @@ impl DefaultBodyLimit {
/// use tower_http::limit::RequestBodyLimitLayer;
/// use http_body::Limited;
///
/// let app: Router<(), Limited<Body>> = Router::new()
/// let app: Router<()> = Router::new()
/// .route("/", get(|body: Bytes| async {}))
/// // Replace the default of 2MB with 1024 bytes.
/// .layer(DefaultBodyLimit::max(1024));

View file

@ -4,7 +4,7 @@
//!
//! [`axum::extract`]: https://docs.rs/axum/latest/axum/extract/index.html
use crate::response::IntoResponse;
use crate::{body::Body, response::IntoResponse};
use async_trait::async_trait;
use http::{request::Parts, Request};
use std::convert::Infallible;
@ -64,48 +64,6 @@ pub trait FromRequestParts<S>: Sized {
///
/// See [`axum::extract`] for more general docs about extractors.
///
/// # 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,
/// http::{self, Request},
/// };
///
/// struct MyExtractor;
///
/// #[async_trait]
/// impl<S, B> FromRequest<S, B> for MyExtractor
/// where
/// // these bounds are required by `async_trait`
/// B: Send + 'static,
/// S: Send + Sync,
/// {
/// type Rejection = http::StatusCode;
///
/// async fn from_request(req: Request<B>, state: &S) -> 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/0.6.0/axum/extract/index.html
#[async_trait]
#[cfg_attr(
@ -114,25 +72,24 @@ pub trait FromRequestParts<S>: Sized {
note = "Function argument is not a valid axum extractor. \nSee `https://docs.rs/axum/latest/axum/extract/index.html` for details",
)
)]
pub trait FromRequest<S, B, M = private::ViaRequest>: Sized {
pub trait FromRequest<S, 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: Request<B>, state: &S) -> Result<Self, Self::Rejection>;
async fn from_request(req: Request<Body>, state: &S) -> Result<Self, Self::Rejection>;
}
#[async_trait]
impl<S, B, T> FromRequest<S, B, private::ViaParts> for T
impl<S, T> FromRequest<S, private::ViaParts> for T
where
B: Send + 'static,
S: Send + Sync,
T: FromRequestParts<S>,
{
type Rejection = <Self as FromRequestParts<S>>::Rejection;
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(req: Request<Body>, state: &S) -> Result<Self, Self::Rejection> {
let (mut parts, _) = req.into_parts();
Self::from_request_parts(&mut parts, state).await
}
@ -155,15 +112,14 @@ where
}
#[async_trait]
impl<S, T, B> FromRequest<S, B> for Option<T>
impl<S, T> FromRequest<S> for Option<T>
where
T: FromRequest<S, B>,
B: Send + 'static,
T: FromRequest<S>,
S: Send + Sync,
{
type Rejection = Infallible;
async fn from_request(req: Request<B>, state: &S) -> Result<Option<T>, Self::Rejection> {
async fn from_request(req: Request<Body>, state: &S) -> Result<Option<T>, Self::Rejection> {
Ok(T::from_request(req, state).await.ok())
}
}
@ -182,15 +138,14 @@ where
}
#[async_trait]
impl<S, T, B> FromRequest<S, B> for Result<T, T::Rejection>
impl<S, T> FromRequest<S> for Result<T, T::Rejection>
where
T: FromRequest<S, B>,
B: Send + 'static,
T: FromRequest<S>,
S: Send + Sync,
{
type Rejection = Infallible;
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(req: Request<Body>, state: &S) -> Result<Self, Self::Rejection> {
Ok(T::from_request(req, state).await)
}
}

View file

@ -3,7 +3,7 @@
use crate::__composite_rejection as composite_rejection;
use crate::__define_rejection as define_rejection;
use crate::BoxError;
use crate::{BoxError, Error};
composite_rejection! {
/// Rejection type for extractors that buffer the request body. Used if the
@ -19,7 +19,11 @@ impl FailedToBufferBody {
where
E: Into<BoxError>,
{
match err.into().downcast::<http_body::LengthLimitError>() {
let box_error = match err.into().downcast::<Error>() {
Ok(err) => err.into_inner(),
Err(err) => err,
};
match box_error.downcast::<http_body::LengthLimitError>() {
Ok(err) => Self::LengthLimitError(LengthLimitError::from_err(err)),
Err(err) => Self::UnknownBodyError(UnknownBodyError::from_err(err)),
}

View file

@ -1,19 +1,18 @@
use super::{rejection::*, FromRequest, FromRequestParts};
use crate::{BoxError, RequestExt};
use crate::{body::Body, RequestExt};
use async_trait::async_trait;
use bytes::Bytes;
use http::{request::Parts, HeaderMap, Method, Request, Uri, Version};
use std::convert::Infallible;
#[async_trait]
impl<S, B> FromRequest<S, B> for Request<B>
impl<S> FromRequest<S> for Request<Body>
where
B: Send,
S: Send + Sync,
{
type Rejection = Infallible;
async fn from_request(req: Request<B>, _: &S) -> Result<Self, Self::Rejection> {
async fn from_request(req: Request<Body>, _: &S) -> Result<Self, Self::Rejection> {
Ok(req)
}
}
@ -72,16 +71,13 @@ where
}
#[async_trait]
impl<S, B> FromRequest<S, B> for Bytes
impl<S> FromRequest<S> for Bytes
where
B: http_body::Body + Send + 'static,
B::Data: Send,
B::Error: Into<BoxError>,
S: Send + Sync,
{
type Rejection = BytesRejection;
async fn from_request(req: Request<B>, _: &S) -> Result<Self, Self::Rejection> {
async fn from_request(req: Request<Body>, _: &S) -> Result<Self, Self::Rejection> {
let bytes = match req.into_limited_body() {
Ok(limited_body) => crate::body::to_bytes(limited_body)
.await
@ -96,16 +92,13 @@ where
}
#[async_trait]
impl<S, B> FromRequest<S, B> for String
impl<S> FromRequest<S> for String
where
B: http_body::Body + Send + 'static,
B::Data: Send,
B::Error: Into<BoxError>,
S: Send + Sync,
{
type Rejection = StringRejection;
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(req: Request<Body>, state: &S) -> Result<Self, Self::Rejection> {
let bytes = Bytes::from_request(req, state)
.await
.map_err(|err| match err {
@ -123,14 +116,13 @@ where
}
#[async_trait]
impl<S, B> FromRequest<S, B> for Parts
impl<S> FromRequest<S> for Parts
where
B: Send + 'static,
S: Send + Sync,
{
type Rejection = Infallible;
async fn from_request(req: Request<B>, _: &S) -> Result<Self, Self::Rejection> {
async fn from_request(req: Request<Body>, _: &S) -> Result<Self, Self::Rejection> {
Ok(req.into_parts().0)
}
}

View file

@ -1,4 +1,5 @@
use super::{FromRequest, FromRequestParts};
use crate::body::Body;
use crate::response::{IntoResponse, Response};
use async_trait::async_trait;
use http::request::{Parts, Request};
@ -45,19 +46,18 @@ macro_rules! impl_from_request {
}
// 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>`.
// implementation of `FromRequest<S, 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,)
impl<S, $($ty,)* $last> FromRequest<S> for ($($ty,)* $last,)
where
$( $ty: FromRequestParts<S> + Send, )*
$last: FromRequest<S, B> + Send,
B: Send + 'static,
$last: FromRequest<S> + Send,
S: Send + Sync,
{
type Rejection = Response;
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(req: Request<Body>, state: &S) -> Result<Self, Self::Rejection> {
let (mut parts, body) = req.into_parts();
$(
@ -85,7 +85,7 @@ mod tests {
fn assert_from_request<M, T>()
where
T: FromRequest<(), http_body::Full<Bytes>, M>,
T: FromRequest<(), M>,
{
}

View file

@ -119,9 +119,7 @@ use std::{
///
/// // `MyBody` can now be returned from handlers.
/// let app = Router::new().route("/", get(|| async { MyBody }));
/// # async {
/// # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// # let _: Router = app;
/// ```
pub trait IntoResponse {
/// Create a response.

View file

@ -255,7 +255,7 @@ mod tests {
custom_key: CustomKey(Key::generate()),
};
let app = Router::<_, Body>::new()
let app = Router::new()
.route("/set", get(set_cookie))
.route("/get", get(get_cookie))
.route("/remove", get(remove_cookie))
@ -352,7 +352,7 @@ mod tests {
custom_key: CustomKey(Key::generate()),
};
let app = Router::<_, Body>::new()
let app = Router::new()
.route("/get", get(get_cookie))
.with_state(state);

View file

@ -1,9 +1,9 @@
use axum::{
async_trait,
body::HttpBody,
body::Body,
extract::{rejection::RawFormRejection, FromRequest, RawForm},
response::{IntoResponse, Response},
BoxError, Error, RequestExt,
Error, RequestExt,
};
use http::{Request, StatusCode};
use serde::de::DeserializeOwned;
@ -46,17 +46,14 @@ pub struct Form<T>(pub T);
axum_core::__impl_deref!(Form);
#[async_trait]
impl<T, S, B> FromRequest<S, B> for Form<T>
impl<T, S> FromRequest<S> for Form<T>
where
T: DeserializeOwned,
B: HttpBody + Send + 'static,
B::Data: Send,
B::Error: Into<BoxError>,
S: Send + Sync,
{
type Rejection = FormRejection;
async fn from_request(req: Request<B>, _state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(req: Request<Body>, _state: &S) -> Result<Self, Self::Rejection> {
let RawForm(bytes) = req
.extract()
.await

View file

@ -4,8 +4,8 @@
use axum::{
async_trait,
body::{Bytes, HttpBody},
extract::{BodyStream, FromRequest},
body::{Body, Bytes},
extract::FromRequest,
response::{IntoResponse, Response},
BoxError, RequestExt,
};
@ -91,22 +91,18 @@ pub struct Multipart {
}
#[async_trait]
impl<S, B> FromRequest<S, B> for Multipart
impl<S> FromRequest<S> for Multipart
where
B: HttpBody + Send + 'static,
B::Data: Into<Bytes>,
B::Error: Into<BoxError>,
S: Send + Sync,
{
type Rejection = MultipartRejection;
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(req: Request<Body>, _state: &S) -> Result<Self, Self::Rejection> {
let boundary = parse_boundary(req.headers()).ok_or(InvalidBoundary)?;
let stream_result = match req.with_limited_body() {
Ok(limited) => BodyStream::from_request(limited, state).await,
Err(unlimited) => BodyStream::from_request(unlimited, state).await,
let stream = match req.with_limited_body() {
Ok(limited) => Body::new(limited),
Err(unlimited) => unlimited.into_body(),
};
let stream = stream_result.unwrap_or_else(|err| match err {});
let multipart = multer::Multipart::new(stream, boundary);
Ok(Self { inner: multipart })
}
@ -452,7 +448,7 @@ mod tests {
// No need for this to be a #[test], we just want to make sure it compiles
fn _multipart_from_request_limited() {
async fn handler(_: Multipart) {}
let _app: Router<(), http_body::Limited<Body>> = Router::new().route("/", post(handler));
let _app: Router<()> = Router::new().route("/", post(handler));
}
#[tokio::test]

View file

@ -1,4 +1,5 @@
use axum::async_trait;
use axum::body::Body;
use axum::extract::{FromRequest, FromRequestParts};
use axum::response::IntoResponse;
use http::request::Parts;
@ -109,16 +110,15 @@ impl<E, R> DerefMut for WithRejection<E, R> {
}
#[async_trait]
impl<B, E, R, S> FromRequest<S, B> for WithRejection<E, R>
impl<E, R, S> FromRequest<S> for WithRejection<E, R>
where
B: Send + 'static,
S: Send + Sync,
E: FromRequest<S, B>,
E: FromRequest<S>,
R: From<E::Rejection> + IntoResponse,
{
type Rejection = R;
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(req: Request<Body>, state: &S) -> Result<Self, Self::Rejection> {
let extractor = E::from_request(req, state).await?;
Ok(WithRejection(extractor, PhantomData))
}
@ -180,7 +180,7 @@ mod tests {
}
}
let req = Request::new(());
let req = Request::new(Body::empty());
let result = WithRejection::<TestExtractor, TestRejection>::from_request(req, &()).await;
assert!(matches!(result, Err(TestRejection)));

View file

@ -1,5 +1,6 @@
//! Additional handler utilities.
use axum::body::Body;
use axum::{
extract::FromRequest,
handler::Handler,
@ -19,15 +20,15 @@ pub use self::or::Or;
///
/// The drawbacks of this trait is that you cannot apply middleware to individual handlers like you
/// can with [`Handler::layer`].
pub trait HandlerCallWithExtractors<T, S, B>: Sized {
pub trait HandlerCallWithExtractors<T, S>: Sized {
/// The type of future calling this handler returns.
type Future: Future<Output = Response> + Send + 'static;
/// Call the handler with the extracted inputs.
fn call(self, extractors: T, state: S) -> <Self as HandlerCallWithExtractors<T, S, B>>::Future;
fn call(self, extractors: T, state: S) -> <Self as HandlerCallWithExtractors<T, S>>::Future;
/// Conver this `HandlerCallWithExtractors` into [`Handler`].
fn into_handler(self) -> IntoHandler<Self, T, S, B> {
fn into_handler(self) -> IntoHandler<Self, T, S> {
IntoHandler {
handler: self,
_marker: PhantomData,
@ -102,9 +103,9 @@ pub trait HandlerCallWithExtractors<T, S, B>: Sized {
/// );
/// # let _: Router = app;
/// ```
fn or<R, Rt>(self, rhs: R) -> Or<Self, R, T, Rt, S, B>
fn or<R, Rt>(self, rhs: R) -> Or<Self, R, T, Rt, S>
where
R: HandlerCallWithExtractors<Rt, S, B>,
R: HandlerCallWithExtractors<Rt, S>,
{
Or {
lhs: self,
@ -117,7 +118,7 @@ pub trait HandlerCallWithExtractors<T, S, B>: Sized {
macro_rules! impl_handler_call_with {
( $($ty:ident),* $(,)? ) => {
#[allow(non_snake_case)]
impl<F, Fut, S, B, $($ty,)*> HandlerCallWithExtractors<($($ty,)*), S, B> for F
impl<F, Fut, S, $($ty,)*> HandlerCallWithExtractors<($($ty,)*), S> for F
where
F: FnOnce($($ty,)*) -> Fut,
Fut: Future + Send + 'static,
@ -130,7 +131,7 @@ macro_rules! impl_handler_call_with {
self,
($($ty,)*): ($($ty,)*),
_state: S,
) -> <Self as HandlerCallWithExtractors<($($ty,)*), S, B>>::Future {
) -> <Self as HandlerCallWithExtractors<($($ty,)*), S>>::Future {
self($($ty,)*).map(IntoResponse::into_response)
}
}
@ -159,22 +160,22 @@ impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,
///
/// Created with [`HandlerCallWithExtractors::into_handler`].
#[allow(missing_debug_implementations)]
pub struct IntoHandler<H, T, S, B> {
pub struct IntoHandler<H, T, S> {
handler: H,
_marker: PhantomData<fn() -> (T, S, B)>,
_marker: PhantomData<fn() -> (T, S)>,
}
impl<H, T, S, B> Handler<T, S, B> for IntoHandler<H, T, S, B>
impl<H, T, S> Handler<T, S> for IntoHandler<H, T, S>
where
H: HandlerCallWithExtractors<T, S, B> + Clone + Send + 'static,
T: FromRequest<S, B> + Send + 'static,
H: HandlerCallWithExtractors<T, S> + Clone + Send + 'static,
T: FromRequest<S> + Send + 'static,
T::Rejection: Send,
B: Send + 'static,
S: Send + Sync + 'static,
{
type Future = BoxFuture<'static, Response>;
fn call(self, req: http::Request<B>, state: S) -> Self::Future {
fn call(self, req: http::Request<Body>, state: S) -> Self::Future {
let req = req.map(Body::new);
Box::pin(async move {
match T::from_request(req, &state).await {
Ok(t) => self.handler.call(t, state).await,
@ -184,9 +185,9 @@ where
}
}
impl<H, T, S, B> Copy for IntoHandler<H, T, S, B> where H: Copy {}
impl<H, T, S> Copy for IntoHandler<H, T, S> where H: Copy {}
impl<H, T, S, B> Clone for IntoHandler<H, T, S, B>
impl<H, T, S> Clone for IntoHandler<H, T, S>
where
H: Clone,
{

View file

@ -1,6 +1,7 @@
use super::HandlerCallWithExtractors;
use crate::either::Either;
use axum::{
body::Body,
extract::{FromRequest, FromRequestParts},
handler::Handler,
http::Request,
@ -14,19 +15,18 @@ use std::{future::Future, marker::PhantomData};
///
/// Created with [`HandlerCallWithExtractors::or`](super::HandlerCallWithExtractors::or).
#[allow(missing_debug_implementations)]
pub struct Or<L, R, Lt, Rt, S, B> {
pub struct Or<L, R, Lt, Rt, S> {
pub(super) lhs: L,
pub(super) rhs: R,
pub(super) _marker: PhantomData<fn() -> (Lt, Rt, S, B)>,
pub(super) _marker: PhantomData<fn() -> (Lt, Rt, S)>,
}
impl<S, B, L, R, Lt, Rt> HandlerCallWithExtractors<Either<Lt, Rt>, S, B> for Or<L, R, Lt, Rt, S, B>
impl<S, L, R, Lt, Rt> HandlerCallWithExtractors<Either<Lt, Rt>, S> for Or<L, R, Lt, Rt, S>
where
L: HandlerCallWithExtractors<Lt, S, B> + Send + 'static,
R: HandlerCallWithExtractors<Rt, S, B> + Send + 'static,
L: HandlerCallWithExtractors<Lt, S> + Send + 'static,
R: HandlerCallWithExtractors<Rt, S> + Send + 'static,
Rt: Send + 'static,
Lt: Send + 'static,
B: Send + 'static,
{
// this puts `futures_util` in our public API but thats fine in axum-extra
type Future = EitherFuture<
@ -38,7 +38,7 @@ where
self,
extractors: Either<Lt, Rt>,
state: S,
) -> <Self as HandlerCallWithExtractors<Either<Lt, Rt>, S, B>>::Future {
) -> <Self as HandlerCallWithExtractors<Either<Lt, Rt>, S>>::Future {
match extractors {
Either::E1(lt) => self
.lhs
@ -54,21 +54,20 @@ where
}
}
impl<S, B, L, R, Lt, Rt, M> Handler<(M, Lt, Rt), S, B> for Or<L, R, Lt, Rt, S, B>
impl<S, L, R, Lt, Rt, M> Handler<(M, Lt, Rt), S> for Or<L, R, Lt, Rt, S>
where
L: HandlerCallWithExtractors<Lt, S, B> + Clone + Send + 'static,
R: HandlerCallWithExtractors<Rt, S, B> + Clone + Send + 'static,
L: HandlerCallWithExtractors<Lt, S> + Clone + Send + 'static,
R: HandlerCallWithExtractors<Rt, S> + Clone + Send + 'static,
Lt: FromRequestParts<S> + Send + 'static,
Rt: FromRequest<S, B, M> + Send + 'static,
Rt: FromRequest<S, M> + Send + 'static,
Lt::Rejection: Send,
Rt::Rejection: Send,
B: Send + 'static,
S: Send + Sync + 'static,
{
// this puts `futures_util` in our public API but thats fine in axum-extra
type Future = BoxFuture<'static, Response>;
fn call(self, req: Request<B>, state: S) -> Self::Future {
fn call(self, req: Request<Body>, state: S) -> Self::Future {
Box::pin(async move {
let (mut parts, body) = req.into_parts();
@ -86,14 +85,14 @@ where
}
}
impl<L, R, Lt, Rt, S, B> Copy for Or<L, R, Lt, Rt, S, B>
impl<L, R, Lt, Rt, S> Copy for Or<L, R, Lt, Rt, S>
where
L: Copy,
R: Copy,
{
}
impl<L, R, Lt, Rt, S, B> Clone for Or<L, R, Lt, Rt, S, B>
impl<L, R, Lt, Rt, S> Clone for Or<L, R, Lt, Rt, S>
where
L: Clone,
R: Clone,

View file

@ -2,12 +2,12 @@
use axum::{
async_trait,
body::{HttpBody, StreamBody},
body::{Body, StreamBody},
extract::FromRequest,
response::{IntoResponse, Response},
BoxError,
};
use bytes::{BufMut, Bytes, BytesMut};
use bytes::{BufMut, BytesMut};
use futures_util::stream::{BoxStream, Stream, TryStream, TryStreamExt};
use http::Request;
use pin_project_lite::pin_project;
@ -101,26 +101,19 @@ impl<S> JsonLines<S, AsResponse> {
}
#[async_trait]
impl<S, B, T> FromRequest<S, B> for JsonLines<T, AsExtractor>
impl<S, T> FromRequest<S> for JsonLines<T, AsExtractor>
where
B: HttpBody + Send + 'static,
B::Data: Into<Bytes>,
B::Error: Into<BoxError>,
T: DeserializeOwned,
S: Send + Sync,
{
type Rejection = Infallible;
async fn from_request(req: Request<B>, _state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(req: Request<Body>, _state: &S) -> Result<Self, Self::Rejection> {
// `Stream::lines` isn't a thing so we have to convert it into an `AsyncRead`
// so we can call `AsyncRead::lines` and then convert it back to a `Stream`
let body = BodyStream {
body: req.into_body(),
};
let body = req.into_body();
let stream = body
.map_ok(Into::into)
.map_err(|err| io::Error::new(io::ErrorKind::Other, err));
let stream = TryStreamExt::map_err(body, |err| io::Error::new(io::ErrorKind::Other, err));
let read = StreamReader::new(stream);
let lines_stream = LinesStream::new(read.lines());
@ -140,26 +133,6 @@ where
}
}
// like `axum::extract::BodyStream` except it doesn't box the inner body
// we don't need that since we box the final stream in `Inner::Extractor`
pin_project! {
struct BodyStream<B> {
#[pin]
body: B,
}
}
impl<B> Stream for BodyStream<B>
where
B: HttpBody + Send + 'static,
{
type Item = Result<B::Data, B::Error>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.project().body.poll_data(cx)
}
}
impl<T> Stream for JsonLines<T, AsExtractor> {
type Item = Result<T, axum::Error>;

View file

@ -2,12 +2,11 @@
use axum::{
async_trait,
body::{Bytes, HttpBody},
body::Body,
extract::{rejection::BytesRejection, FromRequest},
response::{IntoResponse, Response},
BoxError,
};
use bytes::BytesMut;
use bytes::{Bytes, BytesMut};
use http::{Request, StatusCode};
use prost::Message;
@ -97,17 +96,14 @@ use prost::Message;
pub struct Protobuf<T>(pub T);
#[async_trait]
impl<T, S, B> FromRequest<S, B> for Protobuf<T>
impl<T, S> FromRequest<S> for Protobuf<T>
where
T: Message + Default,
B: HttpBody + Send + 'static,
B::Data: Send,
B::Error: Into<BoxError>,
S: Send + Sync,
{
type Rejection = ProtobufRejection;
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(req: Request<Body>, state: &S) -> Result<Self, Self::Rejection> {
let mut bytes = Bytes::from_request(req, state).await?;
match T::decode(&mut bytes) {

View file

@ -1,6 +1,7 @@
//! Additional types for defining routes.
use axum::{
body::Body,
http::Request,
response::{IntoResponse, Redirect, Response},
routing::{any, MethodRouter},
@ -26,7 +27,7 @@ pub use axum_macros::TypedPath;
pub use self::typed::{SecondElementIs, TypedPath};
/// Extension trait that adds additional methods to [`Router`].
pub trait RouterExt<S, B>: sealed::Sealed {
pub trait RouterExt<S>: sealed::Sealed {
/// Add a typed `GET` route to the router.
///
/// The path will be inferred from the first argument to the handler function which must
@ -36,7 +37,7 @@ pub trait RouterExt<S, B>: sealed::Sealed {
#[cfg(feature = "typed-routing")]
fn typed_get<H, T, P>(self, handler: H) -> Self
where
H: axum::handler::Handler<T, S, B>,
H: axum::handler::Handler<T, S>,
T: SecondElementIs<P> + 'static,
P: TypedPath;
@ -49,7 +50,7 @@ pub trait RouterExt<S, B>: sealed::Sealed {
#[cfg(feature = "typed-routing")]
fn typed_delete<H, T, P>(self, handler: H) -> Self
where
H: axum::handler::Handler<T, S, B>,
H: axum::handler::Handler<T, S>,
T: SecondElementIs<P> + 'static,
P: TypedPath;
@ -62,7 +63,7 @@ pub trait RouterExt<S, B>: sealed::Sealed {
#[cfg(feature = "typed-routing")]
fn typed_head<H, T, P>(self, handler: H) -> Self
where
H: axum::handler::Handler<T, S, B>,
H: axum::handler::Handler<T, S>,
T: SecondElementIs<P> + 'static,
P: TypedPath;
@ -75,7 +76,7 @@ pub trait RouterExt<S, B>: sealed::Sealed {
#[cfg(feature = "typed-routing")]
fn typed_options<H, T, P>(self, handler: H) -> Self
where
H: axum::handler::Handler<T, S, B>,
H: axum::handler::Handler<T, S>,
T: SecondElementIs<P> + 'static,
P: TypedPath;
@ -88,7 +89,7 @@ pub trait RouterExt<S, B>: sealed::Sealed {
#[cfg(feature = "typed-routing")]
fn typed_patch<H, T, P>(self, handler: H) -> Self
where
H: axum::handler::Handler<T, S, B>,
H: axum::handler::Handler<T, S>,
T: SecondElementIs<P> + 'static,
P: TypedPath;
@ -101,7 +102,7 @@ pub trait RouterExt<S, B>: sealed::Sealed {
#[cfg(feature = "typed-routing")]
fn typed_post<H, T, P>(self, handler: H) -> Self
where
H: axum::handler::Handler<T, S, B>,
H: axum::handler::Handler<T, S>,
T: SecondElementIs<P> + 'static,
P: TypedPath;
@ -114,7 +115,7 @@ pub trait RouterExt<S, B>: sealed::Sealed {
#[cfg(feature = "typed-routing")]
fn typed_put<H, T, P>(self, handler: H) -> Self
where
H: axum::handler::Handler<T, S, B>,
H: axum::handler::Handler<T, S>,
T: SecondElementIs<P> + 'static,
P: TypedPath;
@ -127,7 +128,7 @@ pub trait RouterExt<S, B>: sealed::Sealed {
#[cfg(feature = "typed-routing")]
fn typed_trace<H, T, P>(self, handler: H) -> Self
where
H: axum::handler::Handler<T, S, B>,
H: axum::handler::Handler<T, S>,
T: SecondElementIs<P> + 'static,
P: TypedPath;
@ -156,7 +157,7 @@ pub trait RouterExt<S, B>: sealed::Sealed {
/// .route_with_tsr("/bar/", get(|| async {}));
/// # let _: Router = app;
/// ```
fn route_with_tsr(self, path: &str, method_router: MethodRouter<S, B>) -> Self
fn route_with_tsr(self, path: &str, method_router: MethodRouter<S>) -> Self
where
Self: Sized;
@ -165,21 +166,20 @@ pub trait RouterExt<S, B>: sealed::Sealed {
/// This works like [`RouterExt::route_with_tsr`] but accepts any [`Service`].
fn route_service_with_tsr<T>(self, path: &str, service: T) -> Self
where
T: Service<Request<B>, Error = Infallible> + Clone + Send + 'static,
T: Service<Request<Body>, Error = Infallible> + Clone + Send + 'static,
T::Response: IntoResponse,
T::Future: Send + 'static,
Self: Sized;
}
impl<S, B> RouterExt<S, B> for Router<S, B>
impl<S> RouterExt<S> for Router<S>
where
B: axum::body::HttpBody + Send + 'static,
S: Clone + Send + Sync + 'static,
{
#[cfg(feature = "typed-routing")]
fn typed_get<H, T, P>(self, handler: H) -> Self
where
H: axum::handler::Handler<T, S, B>,
H: axum::handler::Handler<T, S>,
T: SecondElementIs<P> + 'static,
P: TypedPath,
{
@ -189,7 +189,7 @@ where
#[cfg(feature = "typed-routing")]
fn typed_delete<H, T, P>(self, handler: H) -> Self
where
H: axum::handler::Handler<T, S, B>,
H: axum::handler::Handler<T, S>,
T: SecondElementIs<P> + 'static,
P: TypedPath,
{
@ -199,7 +199,7 @@ where
#[cfg(feature = "typed-routing")]
fn typed_head<H, T, P>(self, handler: H) -> Self
where
H: axum::handler::Handler<T, S, B>,
H: axum::handler::Handler<T, S>,
T: SecondElementIs<P> + 'static,
P: TypedPath,
{
@ -209,7 +209,7 @@ where
#[cfg(feature = "typed-routing")]
fn typed_options<H, T, P>(self, handler: H) -> Self
where
H: axum::handler::Handler<T, S, B>,
H: axum::handler::Handler<T, S>,
T: SecondElementIs<P> + 'static,
P: TypedPath,
{
@ -219,7 +219,7 @@ where
#[cfg(feature = "typed-routing")]
fn typed_patch<H, T, P>(self, handler: H) -> Self
where
H: axum::handler::Handler<T, S, B>,
H: axum::handler::Handler<T, S>,
T: SecondElementIs<P> + 'static,
P: TypedPath,
{
@ -229,7 +229,7 @@ where
#[cfg(feature = "typed-routing")]
fn typed_post<H, T, P>(self, handler: H) -> Self
where
H: axum::handler::Handler<T, S, B>,
H: axum::handler::Handler<T, S>,
T: SecondElementIs<P> + 'static,
P: TypedPath,
{
@ -239,7 +239,7 @@ where
#[cfg(feature = "typed-routing")]
fn typed_put<H, T, P>(self, handler: H) -> Self
where
H: axum::handler::Handler<T, S, B>,
H: axum::handler::Handler<T, S>,
T: SecondElementIs<P> + 'static,
P: TypedPath,
{
@ -249,7 +249,7 @@ where
#[cfg(feature = "typed-routing")]
fn typed_trace<H, T, P>(self, handler: H) -> Self
where
H: axum::handler::Handler<T, S, B>,
H: axum::handler::Handler<T, S>,
T: SecondElementIs<P> + 'static,
P: TypedPath,
{
@ -257,7 +257,7 @@ where
}
#[track_caller]
fn route_with_tsr(mut self, path: &str, method_router: MethodRouter<S, B>) -> Self
fn route_with_tsr(mut self, path: &str, method_router: MethodRouter<S>) -> Self
where
Self: Sized,
{
@ -269,7 +269,7 @@ where
#[track_caller]
fn route_service_with_tsr<T>(mut self, path: &str, service: T) -> Self
where
T: Service<Request<B>, Error = Infallible> + Clone + Send + 'static,
T: Service<Request<Body>, Error = Infallible> + Clone + Send + 'static,
T::Response: IntoResponse,
T::Future: Send + 'static,
Self: Sized,
@ -287,9 +287,8 @@ fn validate_tsr_path(path: &str) {
}
}
fn add_tsr_redirect_route<S, B>(router: Router<S, B>, path: &str) -> Router<S, B>
fn add_tsr_redirect_route<S>(router: Router<S>, path: &str) -> Router<S>
where
B: axum::body::HttpBody + Send + 'static,
S: Clone + Send + Sync + 'static,
{
async fn redirect_handler(uri: Uri) -> Response {
@ -337,7 +336,7 @@ where
mod sealed {
pub trait Sealed {}
impl<S, B> Sealed for axum::Router<S, B> {}
impl<S> Sealed for axum::Router<S> {}
}
#[cfg(test)]

View file

@ -1,5 +1,4 @@
use axum::{
body::Body,
handler::Handler,
routing::{delete, get, on, post, MethodFilter, MethodRouter},
Router,
@ -34,14 +33,13 @@ use axum::{
/// ```
#[derive(Debug)]
#[must_use]
pub struct Resource<S = (), B = Body> {
pub struct Resource<S = ()> {
pub(crate) name: String,
pub(crate) router: Router<S, B>,
pub(crate) router: Router<S>,
}
impl<S, B> Resource<S, B>
impl<S> Resource<S>
where
B: axum::body::HttpBody + Send + 'static,
S: Clone + Send + Sync + 'static,
{
/// Create a `Resource` with the given name.
@ -57,7 +55,7 @@ where
/// Add a handler at `GET /{resource_name}`.
pub fn index<H, T>(self, handler: H) -> Self
where
H: Handler<T, S, B>,
H: Handler<T, S>,
T: 'static,
{
let path = self.index_create_path();
@ -67,7 +65,7 @@ where
/// Add a handler at `POST /{resource_name}`.
pub fn create<H, T>(self, handler: H) -> Self
where
H: Handler<T, S, B>,
H: Handler<T, S>,
T: 'static,
{
let path = self.index_create_path();
@ -77,7 +75,7 @@ where
/// Add a handler at `GET /{resource_name}/new`.
pub fn new<H, T>(self, handler: H) -> Self
where
H: Handler<T, S, B>,
H: Handler<T, S>,
T: 'static,
{
let path = format!("/{}/new", self.name);
@ -87,7 +85,7 @@ where
/// Add a handler at `GET /{resource_name}/:{resource_name}_id`.
pub fn show<H, T>(self, handler: H) -> Self
where
H: Handler<T, S, B>,
H: Handler<T, S>,
T: 'static,
{
let path = self.show_update_destroy_path();
@ -97,7 +95,7 @@ where
/// Add a handler at `GET /{resource_name}/:{resource_name}_id/edit`.
pub fn edit<H, T>(self, handler: H) -> Self
where
H: Handler<T, S, B>,
H: Handler<T, S>,
T: 'static,
{
let path = format!("/{0}/:{0}_id/edit", self.name);
@ -107,7 +105,7 @@ where
/// Add a handler at `PUT or PATCH /resource_name/:{resource_name}_id`.
pub fn update<H, T>(self, handler: H) -> Self
where
H: Handler<T, S, B>,
H: Handler<T, S>,
T: 'static,
{
let path = self.show_update_destroy_path();
@ -117,7 +115,7 @@ where
/// Add a handler at `DELETE /{resource_name}/:{resource_name}_id`.
pub fn destroy<H, T>(self, handler: H) -> Self
where
H: Handler<T, S, B>,
H: Handler<T, S>,
T: 'static,
{
let path = self.show_update_destroy_path();
@ -132,14 +130,14 @@ where
format!("/{0}/:{0}_id", self.name)
}
fn route(mut self, path: &str, method_router: MethodRouter<S, B>) -> Self {
fn route(mut self, path: &str, method_router: MethodRouter<S>) -> Self {
self.router = self.router.route(path, method_router);
self
}
}
impl<S, B> From<Resource<S, B>> for Router<S, B> {
fn from(resource: Resource<S, B>) -> Self {
impl<S> From<Resource<S>> for Router<S> {
fn from(resource: Resource<S>) -> Self {
resource.router
}
}
@ -148,9 +146,9 @@ impl<S, B> From<Resource<S, B>> for Router<S, B> {
mod tests {
#[allow(unused_imports)]
use super::*;
use axum::{extract::Path, http::Method, Router};
use axum::{body::Body, extract::Path, http::Method, Router};
use http::Request;
use tower::{Service, ServiceExt};
use tower::ServiceExt;
#[tokio::test]
async fn works() {
@ -208,10 +206,8 @@ mod tests {
async fn call_route(app: &mut Router, method: Method, uri: &str) -> String {
let res = app
.ready()
.await
.unwrap()
.call(
.clone()
.oneshot(
Request::builder()
.method(method)
.uri(uri)

View file

@ -7,7 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
# Unreleased
- None.
- **breaking:** `#[debug_handler]` no longer accepts a `body = _` argument. The
body type is always `axum::body::Body` ([#1751])
[#1751]: https://github.com/tokio-rs/axum/pull/1751
# 0.3.7 (22. March, 2023)

View file

@ -6,14 +6,10 @@ use crate::{
};
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote, quote_spanned};
use syn::{parse::Parse, parse_quote, spanned::Spanned, FnArg, ItemFn, Token, Type};
use syn::{parse::Parse, spanned::Spanned, FnArg, ItemFn, Token, Type};
pub(crate) fn expand(attr: Attrs, item_fn: ItemFn) -> TokenStream {
let Attrs { body_ty, state_ty } = attr;
let body_ty = body_ty
.map(second)
.unwrap_or_else(|| parse_quote!(axum::body::Body));
let Attrs { state_ty } = attr;
let mut state_ty = state_ty.map(second);
@ -57,7 +53,7 @@ pub(crate) fn expand(attr: Attrs, item_fn: ItemFn) -> TokenStream {
}
} else {
let check_inputs_impls_from_request =
check_inputs_impls_from_request(&item_fn, &body_ty, state_ty);
check_inputs_impls_from_request(&item_fn, state_ty);
quote! {
#check_inputs_impls_from_request
@ -88,20 +84,16 @@ mod kw {
}
pub(crate) struct Attrs {
body_ty: Option<(kw::body, Type)>,
state_ty: Option<(kw::state, Type)>,
}
impl Parse for Attrs {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut body_ty = None;
let mut state_ty = None;
while !input.is_empty() {
let lh = input.lookahead1();
if lh.peek(kw::body) {
parse_assignment_attribute(input, &mut body_ty)?;
} else if lh.peek(kw::state) {
if lh.peek(kw::state) {
parse_assignment_attribute(input, &mut state_ty)?;
} else {
return Err(lh.error());
@ -110,7 +102,7 @@ impl Parse for Attrs {
let _ = input.parse::<Token![,]>();
}
Ok(Self { body_ty, state_ty })
Ok(Self { state_ty })
}
}
@ -183,11 +175,7 @@ fn is_self_pat_type(typed: &syn::PatType) -> bool {
ident == "self"
}
fn check_inputs_impls_from_request(
item_fn: &ItemFn,
body_ty: &Type,
state_ty: Type,
) -> TokenStream {
fn check_inputs_impls_from_request(item_fn: &ItemFn, state_ty: Type) -> TokenStream {
let takes_self = item_fn.sig.inputs.first().map_or(false, |arg| match arg {
FnArg::Receiver(_) => true,
FnArg::Typed(typed) => is_self_pat_type(typed),
@ -266,11 +254,11 @@ fn check_inputs_impls_from_request(
}
} else if consumes_request {
quote_spanned! {span=>
#ty: ::axum::extract::FromRequest<#state_ty, #body_ty> + Send
#ty: ::axum::extract::FromRequest<#state_ty> + Send
}
} else {
quote_spanned! {span=>
#ty: ::axum::extract::FromRequest<#state_ty, #body_ty, M> + Send
#ty: ::axum::extract::FromRequest<#state_ty, M> + Send
}
};
@ -379,7 +367,6 @@ fn request_consuming_type_name(ty: &Type) -> Option<&'static str> {
let type_name = match &*ident.to_string() {
"Json" => "Json<_>",
"BodyStream" => "BodyStream",
"RawBody" => "RawBody<_>",
"RawForm" => "RawForm",
"Multipart" => "Multipart",

View file

@ -19,13 +19,6 @@ pub(crate) enum Trait {
}
impl Trait {
fn body_type(&self) -> impl Iterator<Item = Type> {
match self {
Trait::FromRequest => Some(parse_quote!(B)).into_iter(),
Trait::FromRequestParts => None.into_iter(),
}
}
fn via_marker_type(&self) -> Option<Type> {
match self {
Trait::FromRequest => Some(parse_quote!(M)),
@ -370,14 +363,12 @@ fn impl_struct_by_extracting_each_field(
quote!(::axum::response::Response)
};
let impl_generics = tr
.body_type()
.chain(state.impl_generics())
let impl_generics = state
.impl_generics()
.collect::<Punctuated<Type, Token![,]>>();
let trait_generics = state
.trait_generics()
.chain(tr.body_type())
.collect::<Punctuated<Type, Token![,]>>();
let state_bounds = state.bounds();
@ -388,15 +379,12 @@ fn impl_struct_by_extracting_each_field(
#[automatically_derived]
impl<#impl_generics> ::axum::extract::FromRequest<#trait_generics> for #ident
where
B: ::axum::body::HttpBody + ::std::marker::Send + 'static,
B::Data: ::std::marker::Send,
B::Error: ::std::convert::Into<::axum::BoxError>,
#state_bounds
{
type Rejection = #rejection_ident;
async fn from_request(
mut req: ::axum::http::Request<B>,
mut req: ::axum::http::Request<::axum::body::Body>,
state: &#state,
) -> ::std::result::Result<Self, Self::Rejection> {
#trait_fn_body
@ -749,7 +737,7 @@ fn impl_struct_by_extracting_all_at_once(
// struct AppState {}
// ```
//
// we need to implement `impl<B, M> FromRequest<AppState, B, M>` but only for
// we need to implement `impl<M> FromRequest<AppState, M>` but only for
// - `#[derive(FromRequest)]`, not `#[derive(FromRequestParts)]`
// - `State`, not other extractors
//
@ -760,16 +748,15 @@ fn impl_struct_by_extracting_all_at_once(
None
};
let impl_generics = tr
.body_type()
.chain(via_marker_type.clone())
let impl_generics = via_marker_type
.iter()
.cloned()
.chain(state.impl_generics())
.chain(generic_ident.is_some().then(|| parse_quote!(T)))
.collect::<Punctuated<Type, Token![,]>>();
let trait_generics = state
.trait_generics()
.chain(tr.body_type())
.chain(via_marker_type)
.collect::<Punctuated<Type, Token![,]>>();
@ -828,13 +815,12 @@ fn impl_struct_by_extracting_all_at_once(
where
#via_path<#via_type_generics>: ::axum::extract::FromRequest<#trait_generics>,
#rejection_bound
B: ::std::marker::Send + 'static,
#state_bounds
{
type Rejection = #associated_rejection_type;
async fn from_request(
req: ::axum::http::Request<B>,
req: ::axum::http::Request<::axum::body::Body>,
state: &#state,
) -> ::std::result::Result<Self, Self::Rejection> {
::axum::extract::FromRequest::from_request(req, state)
@ -923,14 +909,12 @@ fn impl_enum_by_extracting_all_at_once(
let path_span = path.span();
let impl_generics = tr
.body_type()
.chain(state.impl_generics())
let impl_generics = state
.impl_generics()
.collect::<Punctuated<Type, Token![,]>>();
let trait_generics = state
.trait_generics()
.chain(tr.body_type())
.collect::<Punctuated<Type, Token![,]>>();
let state_bounds = state.bounds();
@ -942,15 +926,12 @@ fn impl_enum_by_extracting_all_at_once(
#[automatically_derived]
impl<#impl_generics> ::axum::extract::FromRequest<#trait_generics> for #ident
where
B: ::axum::body::HttpBody + ::std::marker::Send + 'static,
B::Data: ::std::marker::Send,
B::Error: ::std::convert::Into<::axum::BoxError>,
#state_bounds
{
type Rejection = #associated_rejection_type;
async fn from_request(
req: ::axum::http::Request<B>,
req: ::axum::http::Request<::axum::body::Body>,
state: &#state,
) -> ::std::result::Result<Self, Self::Rejection> {
::axum::extract::FromRequest::from_request(req, state)

View file

@ -148,7 +148,7 @@ use from_request::Trait::{FromRequest, FromRequestParts};
/// ```
/// pub struct ViaExtractor<T>(pub T);
///
/// // impl<T, S, B> FromRequest<S, B> for ViaExtractor<T> { ... }
/// // impl<T, S> FromRequest<S> for ViaExtractor<T> { ... }
/// ```
///
/// More complex via extractors are not supported and require writing a manual implementation.
@ -481,21 +481,6 @@ pub fn derive_from_request_parts(item: TokenStream) -> TokenStream {
/// }
/// ```
///
/// # Changing request body type
///
/// By default `#[debug_handler]` assumes your request body type is `axum::body::Body`. This will
/// work for most extractors but, for example, it wont work for `Request<axum::body::BoxBody>`,
/// which only implements `FromRequest<BoxBody>` and _not_ `FromRequest<Body>`.
///
/// To work around that the request body type can be customized like so:
///
/// ```
/// use axum::{body::BoxBody, http::Request, debug_handler};
///
/// #[debug_handler(body = BoxBody)]
/// async fn handler(request: Request<BoxBody>) {}
/// ```
///
/// # Changing state type
///
/// By default `#[debug_handler]` assumes your state type is `()` unless your handler has a

View file

@ -16,7 +16,7 @@ error[E0277]: the trait bound `bool: FromRequestParts<()>` is not satisfied
<(T1, T2, T3, T4, T5, T6, T7) as FromRequestParts<S>>
<(T1, T2, T3, T4, T5, T6, T7, T8) as FromRequestParts<S>>
and 26 others
= note: required for `bool` to implement `FromRequest<(), Body, axum_core::extract::private::ViaParts>`
= note: required for `bool` to implement `FromRequest<(), axum_core::extract::private::ViaParts>`
note: required by a bound in `__axum_macros_check_handler_0_from_request_check`
--> tests/debug_handler/fail/argument_not_extractor.rs:4:23
|

View file

@ -1,9 +1,6 @@
use axum_macros::debug_handler;
#[debug_handler(body = BoxBody, body = BoxBody)]
#[debug_handler(state = (), state = ())]
async fn handler() {}
#[debug_handler(state = (), state = ())]
async fn handler_2() {}
fn main() {}

View file

@ -1,11 +1,5 @@
error: `body` specified more than once
--> tests/debug_handler/fail/duplicate_args.rs:3:33
|
3 | #[debug_handler(body = BoxBody, body = BoxBody)]
| ^^^^
error: `state` specified more than once
--> tests/debug_handler/fail/duplicate_args.rs:6:29
--> tests/debug_handler/fail/duplicate_args.rs:3:29
|
6 | #[debug_handler(state = (), state = ())]
3 | #[debug_handler(state = (), state = ())]
| ^^^^^

View file

@ -8,14 +8,13 @@ use axum_macros::debug_handler;
struct A;
#[async_trait]
impl<S, B> FromRequest<S, B> for A
impl<S> FromRequest<S> for A
where
B: Send + 'static,
S: Send + Sync,
{
type Rejection = ();
async fn from_request(_req: Request<B>, _state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(_req: Request<axum::body::Body>, _state: &S) -> Result<Self, Self::Rejection> {
unimplemented!()
}
}

View file

@ -1,5 +1,5 @@
error: Handlers must only take owned values
--> tests/debug_handler/fail/extract_self_mut.rs:25:22
--> tests/debug_handler/fail/extract_self_mut.rs:24:22
|
25 | async fn handler(&mut self) {}
24 | async fn handler(&mut self) {}
| ^^^^^^^^^

View file

@ -8,14 +8,13 @@ use axum_macros::debug_handler;
struct A;
#[async_trait]
impl<S, B> FromRequest<S, B> for A
impl<S> FromRequest<S> for A
where
B: Send + 'static,
S: Send + Sync,
{
type Rejection = ();
async fn from_request(_req: Request<B>, _state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(_req: Request<axum::body::Body>, _state: &S) -> Result<Self, Self::Rejection> {
unimplemented!()
}
}

View file

@ -1,5 +1,5 @@
error: Handlers must only take owned values
--> tests/debug_handler/fail/extract_self_ref.rs:25:22
--> tests/debug_handler/fail/extract_self_ref.rs:24:22
|
25 | async fn handler(&self) {}
24 | async fn handler(&self) {}
| ^^^^^

View file

@ -1,4 +1,4 @@
error: expected `body` or `state`
error: expected `state`
--> tests/debug_handler/fail/invalid_attrs.rs:3:17
|
3 | #[debug_handler(foo)]

View file

@ -15,6 +15,6 @@ error[E0277]: the trait bound `for<'de> Struct: serde::de::Deserialize<'de>` is
(T0, T1, T2, T3)
and $N others
= note: required for `Struct` to implement `serde::de::DeserializeOwned`
= note: required for `Json<Struct>` to implement `FromRequest<(), Body>`
= note: required for `Json<Struct>` to implement `FromRequest<()>`
= help: see issue #48214
= help: add `#![feature(trivial_bounds)]` to the crate attributes to enable

View file

@ -1,10 +0,0 @@
use axum::{body::BoxBody, http::Request};
use axum_macros::debug_handler;
#[debug_handler(body = BoxBody)]
async fn handler(_: Request<BoxBody>) {}
#[debug_handler(body = axum::body::BoxBody,)]
async fn handler_with_trailing_comma_and_type_path(_: Request<axum::body::BoxBody>) {}
fn main() {}

View file

@ -8,27 +8,25 @@ use axum_macros::debug_handler;
struct A;
#[async_trait]
impl<S, B> FromRequest<S, B> for A
impl<S> FromRequest<S> for A
where
B: Send + 'static,
S: Send + Sync,
{
type Rejection = ();
async fn from_request(_req: Request<B>, _state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(_req: Request<axum::body::Body>, _state: &S) -> Result<Self, Self::Rejection> {
unimplemented!()
}
}
#[async_trait]
impl<S, B> FromRequest<S, B> for Box<A>
impl<S> FromRequest<S> for Box<A>
where
B: Send + 'static,
S: Send + Sync,
{
type Rejection = ();
async fn from_request(_req: Request<B>, _state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(_req: Request<axum::body::Body>, _state: &S) -> Result<Self, Self::Rejection> {
unimplemented!()
}
}

View file

@ -12,15 +12,14 @@ struct AppState;
struct A;
#[async_trait]
impl<S, B> FromRequest<S, B> for A
impl<S> FromRequest<S> for A
where
B: Send + 'static,
S: Send + Sync,
AppState: FromRef<S>,
{
type Rejection = ();
async fn from_request(_req: Request<B>, _state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(_req: Request<axum::body::Body>, _state: &S) -> Result<Self, Self::Rejection> {
unimplemented!()
}
}

View file

@ -1,8 +1,8 @@
use axum_macros::debug_handler;
use axum::{body::BoxBody, extract::State, http::Request};
use axum::{extract::State, http::Request};
#[debug_handler(state = AppState, body = BoxBody)]
async fn handler(_: State<AppState>, _: Request<BoxBody>) {}
#[debug_handler(state = AppState)]
async fn handler(_: State<AppState>, _: Request<axum::body::Body>) {}
#[derive(Clone)]
struct AppState;

View file

@ -1,4 +1,4 @@
use axum::{body::Body, routing::get, Router};
use axum::{routing::get, Router};
use axum_macros::FromRequest;
#[derive(FromRequest, Clone)]
@ -7,5 +7,5 @@ struct Extractor<T>(T);
async fn foo(_: Extractor<()>) {}
fn main() {
Router::<(), Body>::new().route("/", get(foo));
_ = Router::<()>::new().route("/", get(foo));
}

View file

@ -4,21 +4,21 @@ error: #[derive(FromRequest)] only supports generics when used with #[from_reque
5 | struct Extractor<T>(T);
| ^
error[E0277]: the trait bound `fn(Extractor<()>) -> impl Future<Output = ()> {foo}: Handler<_, _, _>` is not satisfied
--> tests/from_request/fail/generic_without_via.rs:10:46
|
10 | Router::<(), Body>::new().route("/", get(foo));
| --- ^^^ the trait `Handler<_, _, _>` is not implemented for fn item `fn(Extractor<()>) -> impl Future<Output = ()> {foo}`
| |
| required by a bound introduced by this call
|
= note: Consider using `#[axum::debug_handler]` to improve the error message
= help: the following other types implement trait `Handler<T, S, B>`:
<Layered<L, H, T, S, B, B2> as Handler<T, S, B2>>
<MethodRouter<S, B> as Handler<(), S, B>>
error[E0277]: the trait bound `fn(Extractor<()>) -> impl Future<Output = ()> {foo}: Handler<_, _>` is not satisfied
--> tests/from_request/fail/generic_without_via.rs:10:44
|
10 | _ = Router::<()>::new().route("/", get(foo));
| --- ^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(Extractor<()>) -> impl Future<Output = ()> {foo}`
| |
| required by a bound introduced by this call
|
= note: Consider using `#[axum::debug_handler]` to improve the error message
= help: the following other types implement trait `Handler<T, S>`:
<Layered<L, H, T, S> as Handler<T, S>>
<MethodRouter<S> as Handler<(), S>>
note: required by a bound in `axum::routing::get`
--> $WORKSPACE/axum/src/routing/method_routing.rs
|
| top_level_handler_fn!(get, GET);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `axum::routing::get`
= note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info)
--> $WORKSPACE/axum/src/routing/method_routing.rs
|
| top_level_handler_fn!(get, GET);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `axum::routing::get`
= note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info)

View file

@ -1,4 +1,4 @@
use axum::{body::Body, routing::get, Router};
use axum::{routing::get, Router};
use axum_macros::FromRequest;
#[derive(FromRequest, Clone)]
@ -8,5 +8,5 @@ struct Extractor<T>(T);
async fn foo(_: Extractor<()>) {}
fn main() {
Router::<(), Body>::new().route("/", get(foo));
_ = Router::<()>::new().route("/", get(foo));
}

View file

@ -4,21 +4,21 @@ error: #[derive(FromRequest)] only supports generics when used with #[from_reque
6 | struct Extractor<T>(T);
| ^
error[E0277]: the trait bound `fn(Extractor<()>) -> impl Future<Output = ()> {foo}: Handler<_, _, _>` is not satisfied
--> tests/from_request/fail/generic_without_via_rejection.rs:11:46
|
11 | Router::<(), Body>::new().route("/", get(foo));
| --- ^^^ the trait `Handler<_, _, _>` is not implemented for fn item `fn(Extractor<()>) -> impl Future<Output = ()> {foo}`
| |
| required by a bound introduced by this call
|
= note: Consider using `#[axum::debug_handler]` to improve the error message
= help: the following other types implement trait `Handler<T, S, B>`:
<Layered<L, H, T, S, B, B2> as Handler<T, S, B2>>
<MethodRouter<S, B> as Handler<(), S, B>>
error[E0277]: the trait bound `fn(Extractor<()>) -> impl Future<Output = ()> {foo}: Handler<_, _>` is not satisfied
--> tests/from_request/fail/generic_without_via_rejection.rs:11:44
|
11 | _ = Router::<()>::new().route("/", get(foo));
| --- ^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(Extractor<()>) -> impl Future<Output = ()> {foo}`
| |
| required by a bound introduced by this call
|
= note: Consider using `#[axum::debug_handler]` to improve the error message
= help: the following other types implement trait `Handler<T, S>`:
<Layered<L, H, T, S> as Handler<T, S>>
<MethodRouter<S> as Handler<(), S>>
note: required by a bound in `axum::routing::get`
--> $WORKSPACE/axum/src/routing/method_routing.rs
|
| top_level_handler_fn!(get, GET);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `axum::routing::get`
= note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info)
--> $WORKSPACE/axum/src/routing/method_routing.rs
|
| top_level_handler_fn!(get, GET);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `axum::routing::get`
= note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info)

View file

@ -4,18 +4,18 @@ error: cannot use `rejection` without `via`
18 | #[from_request(rejection(MyRejection))]
| ^^^^^^^^^
error[E0277]: the trait bound `fn(MyExtractor) -> impl Future<Output = ()> {handler}: Handler<_, _, _>` is not satisfied
error[E0277]: the trait bound `fn(MyExtractor) -> impl Future<Output = ()> {handler}: Handler<_, _>` is not satisfied
--> tests/from_request/fail/override_rejection_on_enum_without_via.rs:10:50
|
10 | let _: Router = Router::new().route("/", get(handler).post(handler_result));
| --- ^^^^^^^ the trait `Handler<_, _, _>` is not implemented for fn item `fn(MyExtractor) -> impl Future<Output = ()> {handler}`
| --- ^^^^^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(MyExtractor) -> impl Future<Output = ()> {handler}`
| |
| required by a bound introduced by this call
|
= note: Consider using `#[axum::debug_handler]` to improve the error message
= help: the following other types implement trait `Handler<T, S, B>`:
<Layered<L, H, T, S, B, B2> as Handler<T, S, B2>>
<MethodRouter<S, B> as Handler<(), S, B>>
= help: the following other types implement trait `Handler<T, S>`:
<Layered<L, H, T, S> as Handler<T, S>>
<MethodRouter<S> as Handler<(), S>>
note: required by a bound in `axum::routing::get`
--> $WORKSPACE/axum/src/routing/method_routing.rs
|
@ -23,21 +23,21 @@ note: required by a bound in `axum::routing::get`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `axum::routing::get`
= note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `fn(Result<MyExtractor, MyRejection>) -> impl Future<Output = ()> {handler_result}: Handler<_, _, _>` is not satisfied
error[E0277]: the trait bound `fn(Result<MyExtractor, MyRejection>) -> impl Future<Output = ()> {handler_result}: Handler<_, _>` is not satisfied
--> tests/from_request/fail/override_rejection_on_enum_without_via.rs:10:64
|
10 | let _: Router = Router::new().route("/", get(handler).post(handler_result));
| ---- ^^^^^^^^^^^^^^ the trait `Handler<_, _, _>` is not implemented for fn item `fn(Result<MyExtractor, MyRejection>) -> impl Future<Output = ()> {handler_result}`
| ---- ^^^^^^^^^^^^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(Result<MyExtractor, MyRejection>) -> impl Future<Output = ()> {handler_result}`
| |
| required by a bound introduced by this call
|
= note: Consider using `#[axum::debug_handler]` to improve the error message
= help: the following other types implement trait `Handler<T, S, B>`:
<Layered<L, H, T, S, B, B2> as Handler<T, S, B2>>
<MethodRouter<S, B> as Handler<(), S, B>>
note: required by a bound in `MethodRouter::<S, B>::post`
= help: the following other types implement trait `Handler<T, S>`:
<Layered<L, H, T, S> as Handler<T, S>>
<MethodRouter<S> as Handler<(), S>>
note: required by a bound in `MethodRouter::<S>::post`
--> $WORKSPACE/axum/src/routing/method_routing.rs
|
| chained_handler_fn!(post, POST);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `MethodRouter::<S, B>::post`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `MethodRouter::<S>::post`
= note: this error originates in the macro `chained_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info)

View file

@ -15,7 +15,7 @@ struct OtherState {}
fn assert_from_request()
where
Extractor: axum::extract::FromRequest<AppState, axum::body::Body, Rejection = axum::response::Response>,
Extractor: axum::extract::FromRequest<AppState, Rejection = axum::response::Response>,
{
}

View file

@ -1,5 +1,4 @@
use axum::{
body::Body,
extract::{FromRequest, Json},
response::Response,
};
@ -15,7 +14,7 @@ struct Extractor {
fn assert_from_request()
where
Extractor: FromRequest<(), Body, Rejection = Response>,
Extractor: FromRequest<(), Rejection = Response>,
{
}

View file

@ -5,7 +5,7 @@ struct Extractor {}
fn assert_from_request()
where
Extractor: axum::extract::FromRequest<(), axum::body::Body, Rejection = std::convert::Infallible>,
Extractor: axum::extract::FromRequest<(), Rejection = std::convert::Infallible>,
{
}

View file

@ -5,7 +5,7 @@ struct Extractor();
fn assert_from_request()
where
Extractor: axum::extract::FromRequest<(), axum::body::Body, Rejection = std::convert::Infallible>,
Extractor: axum::extract::FromRequest<(), Rejection = std::convert::Infallible>,
{
}

View file

@ -1,4 +1,4 @@
use axum::{body::Body, routing::get, Extension, Router};
use axum::{routing::get, Extension, Router};
use axum_macros::FromRequest;
#[derive(FromRequest, Clone)]
@ -8,5 +8,5 @@ enum Extractor {}
async fn foo(_: Extractor) {}
fn main() {
Router::<(), Body>::new().route("/", get(foo));
_ = Router::<()>::new().route("/", get(foo));
}

View file

@ -1,4 +1,4 @@
use axum::{body::Body, routing::get, Extension, Router};
use axum::{routing::get, Extension, Router};
use axum_macros::FromRequestParts;
#[derive(FromRequestParts, Clone)]
@ -8,5 +8,5 @@ enum Extractor {}
async fn foo(_: Extractor) {}
fn main() {
Router::<(), Body>::new().route("/", get(foo));
_ = Router::<()>::new().route("/", get(foo));
}

View file

@ -1,5 +1,4 @@
use axum::{
body::Body,
extract::{FromRequest, TypedHeader, rejection::TypedHeaderRejection},
response::Response,
headers::{self, UserAgent},
@ -17,7 +16,7 @@ struct Extractor {
fn assert_from_request()
where
Extractor: FromRequest<(), Body, Rejection = Response>,
Extractor: FromRequest<(), Rejection = Response>,
{
}

View file

@ -1,5 +1,4 @@
use axum::{
body::Body,
response::Response,
extract::{
rejection::TypedHeaderRejection,
@ -24,7 +23,7 @@ struct Extractor {
fn assert_from_request()
where
Extractor: FromRequest<(), Body, Rejection = Response>,
Extractor: FromRequest<(), Rejection = Response>,
{
}

View file

@ -4,6 +4,7 @@ use axum::{
http::{StatusCode, Request},
response::{IntoResponse, Response},
routing::get,
body::Body,
Extension, Router,
};
@ -27,15 +28,14 @@ struct MyExtractor {
struct OtherExtractor;
#[async_trait]
impl<S, B> FromRequest<S, B> for OtherExtractor
impl<S> FromRequest<S> for OtherExtractor
where
B: Send + 'static,
S: Send + Sync,
{
// this rejection doesn't implement `Display` and `Error`
type Rejection = (StatusCode, String);
async fn from_request(_req: Request<B>, _state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(_req: Request<Body>, _state: &S) -> Result<Self, Self::Rejection> {
todo!()
}
}

View file

@ -20,7 +20,7 @@ impl FromRef<AppState> for Key {
fn assert_from_request()
where
Extractor: axum::extract::FromRequest<AppState, axum::body::Body, Rejection = axum::response::Response>,
Extractor: axum::extract::FromRequest<AppState, Rejection = axum::response::Response>,
{
}

View file

@ -11,7 +11,7 @@ struct AppState {}
fn assert_from_request()
where
Extractor: axum::extract::FromRequest<AppState, axum::body::Body, Rejection = axum::response::Response>,
Extractor: axum::extract::FromRequest<AppState, Rejection = axum::response::Response>,
{
}

View file

@ -12,7 +12,7 @@ struct AppState {}
fn assert_from_request()
where
Extractor: axum::extract::FromRequest<AppState, axum::body::Body, Rejection = axum::response::Response>,
Extractor: axum::extract::FromRequest<AppState, Rejection = axum::response::Response>,
{
}

View file

@ -5,7 +5,7 @@ struct Extractor(axum::http::HeaderMap, String);
fn assert_from_request()
where
Extractor: axum::extract::FromRequest<(), axum::body::Body>,
Extractor: axum::extract::FromRequest<()>,
{
}

View file

@ -13,7 +13,7 @@ struct Payload {}
fn assert_from_request()
where
Extractor: axum::extract::FromRequest<(), axum::body::Body>,
Extractor: axum::extract::FromRequest<()>,
{
}

View file

@ -14,7 +14,7 @@ struct Payload {}
fn assert_from_request()
where
Extractor: axum::extract::FromRequest<(), axum::body::Body, Rejection = Response>,
Extractor: axum::extract::FromRequest<(), Rejection = Response>,
{
}

View file

@ -9,7 +9,7 @@ struct State;
fn assert_from_request()
where
Extractor: axum::extract::FromRequest<(), axum::body::Body>,
Extractor: axum::extract::FromRequest<()>,
{
}

View file

@ -5,7 +5,7 @@ struct Extractor;
fn assert_from_request()
where
Extractor: axum::extract::FromRequest<(), axum::body::Body, Rejection = std::convert::Infallible>,
Extractor: axum::extract::FromRequest<(), Rejection = std::convert::Infallible>,
{
}

View file

@ -40,7 +40,7 @@ impl Default for MyRejection {
}
fn main() {
axum::Router::<(), axum::body::Body>::new()
_ = axum::Router::<()>::new()
.typed_get(|_: Result<MyPathNamed, MyRejection>| async {})
.typed_post(|_: Result<MyPathUnnamed, MyRejection>| async {})
.typed_put(|_: Result<MyPathUnit, MyRejection>| async {});

View file

@ -9,7 +9,7 @@ struct MyPath {
}
fn main() {
axum::Router::<(), axum::body::Body>::new().route("/", axum::routing::get(|_: MyPath| async {}));
_ = axum::Router::<()>::new().route("/", axum::routing::get(|_: MyPath| async {}));
assert_eq!(MyPath::PATH, "/users/:user_id/teams/:team_id");
assert_eq!(

View file

@ -19,7 +19,7 @@ struct UsersIndex;
async fn result_handler_unit_struct(_: Result<UsersIndex, StatusCode>) {}
fn main() {
axum::Router::<(), axum::body::Body>::new()
_ = axum::Router::<()>::new()
.typed_get(option_handler)
.typed_post(result_handler)
.typed_post(result_handler_unit_struct);

View file

@ -8,7 +8,7 @@ pub type Result<T> = std::result::Result<T, ()>;
struct MyPath(u32, u32);
fn main() {
axum::Router::<(), axum::body::Body>::new().route("/", axum::routing::get(|_: MyPath| async {}));
_ = axum::Router::<()>::new().route("/", axum::routing::get(|_: MyPath| async {}));
assert_eq!(MyPath::PATH, "/users/:user_id/teams/:team_id");
assert_eq!(format!("{}", MyPath(1, 2)), "/users/1/teams/2");

View file

@ -5,7 +5,7 @@ use axum_extra::routing::TypedPath;
struct MyPath;
fn main() {
axum::Router::<(), axum::body::Body>::new()
_ = axum::Router::<()>::new()
.route("/", axum::routing::get(|_: MyPath| async {}));
assert_eq!(MyPath::PATH, "/users");

View file

@ -8,5 +8,5 @@ struct MyPath {
}
fn main() {
axum::Router::<(), axum::body::Body>::new().typed_get(|_: MyPath| async {});
_ = axum::Router::<()>::new().typed_get(|_: MyPath| async {});
}

View file

@ -7,12 +7,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
# Unreleased
- **breaking:** The following types/traits are no longer generic over the request body
(i.e. the `B` type param has been removed) ([#1751]):
- `FromRequest`
- `FromRequestParts`
- `Handler`
- `HandlerService`
- `HandlerWithoutStateExt`
- `Layered`
- `LayeredFuture`
- `MethodRouter`
- `RequestExt`
- `Route`
- `RouteFuture`
- `Router`
- **breaking:** axum no longer re-exports `hyper::Body` as that type is removed
in hyper 1.0. Instead axum has its own body type at `axum::body::Body` ([#1751])
- **breaking:** `extract::BodyStream` has been removed as `body::Body`
implements `Stream` and `FromRequest` directly ([#1751])
- **breaking:** Change `sse::Event::json_data` to use `axum_core::Error` as its error type ([#1762])
- **breaking:** Rename `DefaultOnFailedUpdgrade` to `DefaultOnFailedUpgrade` ([#1664])
- **breaking:** Rename `OnFailedUpdgrade` to `OnFailedUpgrade` ([#1664])
- **added:** Add `Router::as_service` and `Router::into_service` to workaround
type inference issues when calling `ServiceExt` methods on a `Router` ([#1835])
[#1762]: https://github.com/tokio-rs/axum/pull/1762
[#1664]: https://github.com/tokio-rs/axum/pull/1664
[#1751]: https://github.com/tokio-rs/axum/pull/1751
[#1762]: https://github.com/tokio-rs/axum/pull/1762
[#1835]: https://github.com/tokio-rs/axum/pull/1835
# 0.6.16 (18. April, 2023)

View file

@ -7,11 +7,8 @@ pub use self::stream_body::StreamBody;
#[doc(no_inline)]
pub use http_body::{Body as HttpBody, Empty, Full};
#[doc(no_inline)]
pub use hyper::body::Body;
#[doc(no_inline)]
pub use bytes::Bytes;
#[doc(inline)]
pub use axum_core::body::{boxed, BoxBody};
pub use axum_core::body::{boxed, Body, BoxBody};

View file

@ -21,7 +21,7 @@ pin_project! {
///
/// The purpose of this type is to be used in responses. If you want to
/// extract the request body as a stream consider using
/// [`BodyStream`](crate::extract::BodyStream).
/// [`Body`](crate::body::Body).
///
/// # Example
///

View file

@ -1,27 +1,25 @@
use std::{convert::Infallible, fmt};
use axum_core::body::Body;
use http::Request;
use tower::Service;
use crate::{
body::HttpBody,
handler::Handler,
routing::{future::RouteFuture, Route},
Router,
};
pub(crate) struct BoxedIntoRoute<S, B, E>(Box<dyn ErasedIntoRoute<S, B, E>>);
pub(crate) struct BoxedIntoRoute<S, E>(Box<dyn ErasedIntoRoute<S, E>>);
impl<S, B> BoxedIntoRoute<S, B, Infallible>
impl<S> BoxedIntoRoute<S, Infallible>
where
S: Clone + Send + Sync + 'static,
B: Send + 'static,
{
pub(crate) fn from_handler<H, T>(handler: H) -> Self
where
H: Handler<T, S, B>,
H: Handler<T, S>,
T: 'static,
B: HttpBody,
{
Self(Box::new(MakeErasedHandler {
handler,
@ -30,14 +28,12 @@ where
}
}
impl<S, B, E> BoxedIntoRoute<S, B, E> {
pub(crate) fn map<F, B2, E2>(self, f: F) -> BoxedIntoRoute<S, B2, E2>
impl<S, E> BoxedIntoRoute<S, E> {
pub(crate) fn map<F, E2>(self, f: F) -> BoxedIntoRoute<S, E2>
where
S: 'static,
B: 'static,
E: 'static,
F: FnOnce(Route<B, E>) -> Route<B2, E2> + Clone + Send + 'static,
B2: HttpBody + 'static,
F: FnOnce(Route<E>) -> Route<E2> + Clone + Send + 'static,
E2: 'static,
{
BoxedIntoRoute(Box::new(Map {
@ -46,60 +42,59 @@ impl<S, B, E> BoxedIntoRoute<S, B, E> {
}))
}
pub(crate) fn into_route(self, state: S) -> Route<B, E> {
pub(crate) fn into_route(self, state: S) -> Route<E> {
self.0.into_route(state)
}
}
impl<S, B, E> Clone for BoxedIntoRoute<S, B, E> {
impl<S, E> Clone for BoxedIntoRoute<S, E> {
fn clone(&self) -> Self {
Self(self.0.clone_box())
}
}
impl<S, B, E> fmt::Debug for BoxedIntoRoute<S, B, E> {
impl<S, E> fmt::Debug for BoxedIntoRoute<S, E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("BoxedIntoRoute").finish()
}
}
pub(crate) trait ErasedIntoRoute<S, B, E>: Send {
fn clone_box(&self) -> Box<dyn ErasedIntoRoute<S, B, E>>;
pub(crate) trait ErasedIntoRoute<S, E>: Send {
fn clone_box(&self) -> Box<dyn ErasedIntoRoute<S, E>>;
fn into_route(self: Box<Self>, state: S) -> Route<B, E>;
fn into_route(self: Box<Self>, state: S) -> Route<E>;
fn call_with_state(self: Box<Self>, request: Request<B>, state: S) -> RouteFuture<B, E>;
fn call_with_state(self: Box<Self>, request: Request<Body>, state: S) -> RouteFuture<E>;
}
pub(crate) struct MakeErasedHandler<H, S, B> {
pub(crate) struct MakeErasedHandler<H, S> {
pub(crate) handler: H,
pub(crate) into_route: fn(H, S) -> Route<B>,
pub(crate) into_route: fn(H, S) -> Route,
}
impl<H, S, B> ErasedIntoRoute<S, B, Infallible> for MakeErasedHandler<H, S, B>
impl<H, S> ErasedIntoRoute<S, Infallible> for MakeErasedHandler<H, S>
where
H: Clone + Send + 'static,
S: 'static,
B: HttpBody + 'static,
{
fn clone_box(&self) -> Box<dyn ErasedIntoRoute<S, B, Infallible>> {
fn clone_box(&self) -> Box<dyn ErasedIntoRoute<S, Infallible>> {
Box::new(self.clone())
}
fn into_route(self: Box<Self>, state: S) -> Route<B> {
fn into_route(self: Box<Self>, state: S) -> Route {
(self.into_route)(self.handler, state)
}
fn call_with_state(
self: Box<Self>,
request: Request<B>,
request: Request<Body>,
state: S,
) -> RouteFuture<B, Infallible> {
) -> RouteFuture<Infallible> {
self.into_route(state).call(request)
}
}
impl<H, S, B> Clone for MakeErasedHandler<H, S, B>
impl<H, S> Clone for MakeErasedHandler<H, S>
where
H: Clone,
{
@ -111,34 +106,33 @@ where
}
}
pub(crate) struct MakeErasedRouter<S, B> {
pub(crate) router: Router<S, B>,
pub(crate) into_route: fn(Router<S, B>, S) -> Route<B>,
pub(crate) struct MakeErasedRouter<S> {
pub(crate) router: Router<S>,
pub(crate) into_route: fn(Router<S>, S) -> Route,
}
impl<S, B> ErasedIntoRoute<S, B, Infallible> for MakeErasedRouter<S, B>
impl<S> ErasedIntoRoute<S, Infallible> for MakeErasedRouter<S>
where
S: Clone + Send + Sync + 'static,
B: HttpBody + Send + 'static,
{
fn clone_box(&self) -> Box<dyn ErasedIntoRoute<S, B, Infallible>> {
fn clone_box(&self) -> Box<dyn ErasedIntoRoute<S, Infallible>> {
Box::new(self.clone())
}
fn into_route(self: Box<Self>, state: S) -> Route<B> {
fn into_route(self: Box<Self>, state: S) -> Route {
(self.into_route)(self.router, state)
}
fn call_with_state(
mut self: Box<Self>,
request: Request<B>,
request: Request<Body>,
state: S,
) -> RouteFuture<B, Infallible> {
) -> RouteFuture<Infallible> {
self.router.call_with_state(request, state)
}
}
impl<S, B> Clone for MakeErasedRouter<S, B>
impl<S> Clone for MakeErasedRouter<S>
where
S: Clone,
{
@ -150,44 +144,42 @@ where
}
}
pub(crate) struct Map<S, B, E, B2, E2> {
pub(crate) inner: Box<dyn ErasedIntoRoute<S, B, E>>,
pub(crate) layer: Box<dyn LayerFn<B, E, B2, E2>>,
pub(crate) struct Map<S, E, E2> {
pub(crate) inner: Box<dyn ErasedIntoRoute<S, E>>,
pub(crate) layer: Box<dyn LayerFn<E, E2>>,
}
impl<S, B, E, B2, E2> ErasedIntoRoute<S, B2, E2> for Map<S, B, E, B2, E2>
impl<S, E, E2> ErasedIntoRoute<S, E2> for Map<S, E, E2>
where
S: 'static,
B: 'static,
E: 'static,
B2: HttpBody + 'static,
E2: 'static,
{
fn clone_box(&self) -> Box<dyn ErasedIntoRoute<S, B2, E2>> {
fn clone_box(&self) -> Box<dyn ErasedIntoRoute<S, E2>> {
Box::new(Self {
inner: self.inner.clone_box(),
layer: self.layer.clone_box(),
})
}
fn into_route(self: Box<Self>, state: S) -> Route<B2, E2> {
fn into_route(self: Box<Self>, state: S) -> Route<E2> {
(self.layer)(self.inner.into_route(state))
}
fn call_with_state(self: Box<Self>, request: Request<B2>, state: S) -> RouteFuture<B2, E2> {
fn call_with_state(self: Box<Self>, request: Request<Body>, state: S) -> RouteFuture<E2> {
(self.layer)(self.inner.into_route(state)).call(request)
}
}
pub(crate) trait LayerFn<B, E, B2, E2>: FnOnce(Route<B, E>) -> Route<B2, E2> + Send {
fn clone_box(&self) -> Box<dyn LayerFn<B, E, B2, E2>>;
pub(crate) trait LayerFn<E, E2>: FnOnce(Route<E>) -> Route<E2> + Send {
fn clone_box(&self) -> Box<dyn LayerFn<E, E2>>;
}
impl<F, B, E, B2, E2> LayerFn<B, E, B2, E2> for F
impl<F, E, E2> LayerFn<E, E2> for F
where
F: FnOnce(Route<B, E>) -> Route<B2, E2> + Clone + Send + 'static,
F: FnOnce(Route<E>) -> Route<E2> + Clone + Send + 'static,
{
fn clone_box(&self) -> Box<dyn LayerFn<B, E, B2, E2>> {
fn clone_box(&self) -> Box<dyn LayerFn<E, E2>> {
Box::new(self.clone())
}
}

View file

@ -188,7 +188,7 @@ async fn handler(
// ...
}
#
# let _: axum::routing::MethodRouter<AppState, String> = axum::routing::get(handler);
# let _: axum::routing::MethodRouter<AppState> = axum::routing::get(handler);
```
We get a compile error if `String` isn't the last extractor:
@ -466,7 +466,7 @@ use axum::{
async_trait,
extract::FromRequest,
response::{Response, IntoResponse},
body::Bytes,
body::{Bytes, Body},
routing::get,
Router,
http::{
@ -479,15 +479,14 @@ use axum::{
struct ValidatedBody(Bytes);
#[async_trait]
impl<S, B> FromRequest<S, B> for ValidatedBody
impl<S> FromRequest<S> for ValidatedBody
where
Bytes: FromRequest<S, B>,
B: Send + 'static,
Bytes: FromRequest<S>,
S: Send + Sync,
{
type Rejection = Response;
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(req: Request<Body>, state: &S) -> Result<Self, Self::Rejection> {
let body = Bytes::from_request(req, state)
.await
.map_err(IntoResponse::into_response)?;
@ -520,6 +519,7 @@ use axum::{
routing::get,
extract::{FromRequest, FromRequestParts},
http::{Request, request::Parts},
body::Body,
async_trait,
};
use std::convert::Infallible;
@ -529,14 +529,13 @@ struct MyExtractor;
// `MyExtractor` implements both `FromRequest`
#[async_trait]
impl<S, B> FromRequest<S, B> for MyExtractor
impl<S> FromRequest<S> for MyExtractor
where
S: Send + Sync,
B: Send + 'static,
{
type Rejection = Infallible;
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(req: Request<Body>, state: &S) -> Result<Self, Self::Rejection> {
// ...
# todo!()
}
@ -639,81 +638,6 @@ For security reasons, [`Bytes`] will, by default, not accept bodies larger than
For more details, including how to disable this limit, see [`DefaultBodyLimit`].
# Request body extractors
Most of the time your request body type will be [`body::Body`] (a re-export
of [`hyper::Body`]), which is directly supported by all extractors.
However if you're applying a tower middleware that changes the request body type
you might have to apply a different body type to some extractors:
```rust
use std::{
task::{Context, Poll},
pin::Pin,
};
use tower_http::map_request_body::MapRequestBodyLayer;
use axum::{
extract::{self, BodyStream},
body::{Body, HttpBody},
routing::get,
http::{header::HeaderMap, Request},
Router,
};
struct MyBody<B>(B);
impl<B> HttpBody for MyBody<B>
where
B: HttpBody + Unpin,
{
type Data = B::Data;
type Error = B::Error;
fn poll_data(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
Pin::new(&mut self.0).poll_data(cx)
}
fn poll_trailers(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
Pin::new(&mut self.0).poll_trailers(cx)
}
}
let app = Router::new()
.route(
"/string",
// `String` works directly with any body type
get(|_: String| async {})
)
.route(
"/body",
// `extract::Body` defaults to `axum::body::Body`
// but can be customized
get(|_: extract::RawBody<MyBody<Body>>| async {})
)
.route(
"/body-stream",
// same for `extract::BodyStream`
get(|_: extract::BodyStream| async {}),
)
.route(
// and `Request<_>`
"/request",
get(|_: Request<MyBody<Body>>| async {})
)
// middleware that changes the request body type
.layer(MapRequestBodyLayer::new(MyBody));
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
```
# Running extractors from middleware
Extractors can also be run from middleware:
@ -759,7 +683,7 @@ fn token_is_valid(token: &str) -> bool {
}
let app = Router::new().layer(middleware::from_fn(auth_middleware));
# let _: Router<()> = app;
# let _: Router = app;
```
# Wrapping extractors
@ -771,6 +695,7 @@ may or may not consume the request body) you should implement both
```rust
use axum::{
Router,
body::Body,
routing::get,
extract::{FromRequest, FromRequestParts},
http::{Request, HeaderMap, request::Parts},
@ -806,15 +731,14 @@ where
// and `FromRequest`
#[async_trait]
impl<S, B, T> FromRequest<S, B> for Timing<T>
impl<S, T> FromRequest<S> for Timing<T>
where
B: Send + 'static,
S: Send + Sync,
T: FromRequest<S, B>,
T: FromRequest<S>,
{
type Rejection = T::Rejection;
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(req: Request<Body>, state: &S) -> Result<Self, Self::Rejection> {
let start = Instant::now();
let extractor = T::from_request(req, state).await?;
let duration = start.elapsed();

View file

@ -97,7 +97,7 @@ let app = Router::new()
.layer(layer_one)
.layer(layer_two)
.layer(layer_three);
# let _: Router<(), axum::body::Body> = app;
# let _: Router = app;
```
Think of the middleware as being layered like an onion where each new layer
@ -156,7 +156,7 @@ let app = Router::new()
.layer(layer_two)
.layer(layer_three),
);
# let _: Router<(), axum::body::Body> = app;
# let _: Router = app;
```
`ServiceBuilder` works by composing all layers into one such that they run top
@ -523,7 +523,7 @@ async fn handler(
let app = Router::new()
.route("/", get(handler))
.route_layer(middleware::from_fn(auth));
# let _: Router<()> = app;
# let _: Router = app;
```
[Response extensions] can also be used but note that request extensions are not
@ -549,13 +549,13 @@ use axum::{
http::Request,
};
async fn rewrite_request_uri<B>(req: Request<B>, next: Next<B>) -> Response {
fn rewrite_request_uri<B>(req: Request<B>) -> Request<B> {
// ...
# next.run(req).await
# req
}
// this can be any `tower::Layer`
let middleware = axum::middleware::from_fn(rewrite_request_uri);
let middleware = tower::util::MapRequestLayer::new(rewrite_request_uri);
let app = Router::new();

View file

@ -29,7 +29,7 @@ pub use self::{
path::{Path, RawPathParams},
raw_form::RawForm,
raw_query::RawQuery,
request_parts::{BodyStream, RawBody},
request_parts::RawBody,
state::State,
};

View file

@ -2,13 +2,13 @@
//!
//! See [`Multipart`] for more details.
use super::{BodyStream, FromRequest};
use crate::body::{Bytes, HttpBody};
use crate::BoxError;
use super::FromRequest;
use crate::body::Bytes;
use async_trait::async_trait;
use axum_core::__composite_rejection as composite_rejection;
use axum_core::__define_rejection as define_rejection;
use axum_core::response::{IntoResponse, Response};
use axum_core::body::Body;
use axum_core::RequestExt;
use futures_util::stream::Stream;
use http::header::{HeaderMap, CONTENT_TYPE};
@ -59,22 +59,18 @@ pub struct Multipart {
}
#[async_trait]
impl<S, B> FromRequest<S, B> for Multipart
impl<S> FromRequest<S> for Multipart
where
B: HttpBody + Send + 'static,
B::Data: Into<Bytes>,
B::Error: Into<BoxError>,
S: Send + Sync,
{
type Rejection = MultipartRejection;
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(req: Request<Body>, _state: &S) -> Result<Self, Self::Rejection> {
let boundary = parse_boundary(req.headers()).ok_or(InvalidBoundary)?;
let stream_result = match req.with_limited_body() {
Ok(limited) => BodyStream::from_request(limited, state).await,
Err(unlimited) => BodyStream::from_request(unlimited, state).await,
let stream = match req.with_limited_body() {
Ok(limited) => Body::new(limited),
Err(unlimited) => unlimited.into_body(),
};
let stream = stream_result.unwrap_or_else(|err| match err {});
let multipart = multer::Multipart::new(stream, boundary);
Ok(Self { inner: multipart })
}
@ -310,7 +306,7 @@ mod tests {
use axum_core::extract::DefaultBodyLimit;
use super::*;
use crate::{body::Body, response::IntoResponse, routing::post, test_helpers::*, Router};
use crate::{response::IntoResponse, routing::post, test_helpers::*, Router};
#[crate::test]
async fn content_type_with_encoding() {
@ -346,7 +342,9 @@ mod tests {
// No need for this to be a #[test], we just want to make sure it compiles
fn _multipart_from_request_limited() {
async fn handler(_: Multipart) {}
let _app: Router<(), http_body::Limited<Body>> = Router::new().route("/", post(handler));
let _app: Router = Router::new()
.route("/", post(handler))
.layer(tower_http::limit::RequestBodyLimitLayer::new(1024));
}
#[crate::test]

View file

@ -71,7 +71,7 @@ mod tests {
use crate::{routing::get, test_helpers::TestClient, Router};
use super::*;
use axum_core::extract::FromRequest;
use axum_core::{body::Body, extract::FromRequest};
use http::{Request, StatusCode};
use serde::Deserialize;
use std::fmt::Debug;
@ -80,7 +80,10 @@ mod tests {
where
T: DeserializeOwned + PartialEq + Debug,
{
let req = Request::builder().uri(uri.as_ref()).body(()).unwrap();
let req = Request::builder()
.uri(uri.as_ref())
.body(Body::empty())
.unwrap();
assert_eq!(Query::<T>::from_request(req, &()).await.unwrap().0, value);
}

View file

@ -1,5 +1,5 @@
use async_trait::async_trait;
use axum_core::extract::FromRequest;
use axum_core::{body::Body, extract::FromRequest};
use bytes::{Bytes, BytesMut};
use http::{Method, Request};
@ -8,8 +8,6 @@ use super::{
rejection::{InvalidFormContentType, RawFormRejection},
};
use crate::{body::HttpBody, BoxError};
/// Extractor that extracts raw form requests.
///
/// For `GET` requests it will extract the raw query. For other methods it extracts the raw
@ -35,16 +33,13 @@ use crate::{body::HttpBody, BoxError};
pub struct RawForm(pub Bytes);
#[async_trait]
impl<S, B> FromRequest<S, B> for RawForm
impl<S> FromRequest<S> for RawForm
where
B: HttpBody + Send + 'static,
B::Data: Send,
B::Error: Into<BoxError>,
S: Send + Sync,
{
type Rejection = RawFormRejection;
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(req: Request<Body>, state: &S) -> Result<Self, Self::Rejection> {
if req.method() == Method::GET {
let mut bytes = BytesMut::new();
@ -65,20 +60,15 @@ where
#[cfg(test)]
mod tests {
use axum_core::body::Body;
use http::{header::CONTENT_TYPE, Request};
use super::{InvalidFormContentType, RawForm, RawFormRejection};
use crate::{
body::{Bytes, Empty, Full},
extract::FromRequest,
};
use crate::extract::FromRequest;
async fn check_query(uri: &str, value: &[u8]) {
let req = Request::builder()
.uri(uri)
.body(Empty::<Bytes>::new())
.unwrap();
let req = Request::builder().uri(uri).body(Body::empty()).unwrap();
assert_eq!(RawForm::from_request(req, &()).await.unwrap().0, value);
}
@ -86,7 +76,7 @@ mod tests {
async fn check_body(body: &'static [u8]) {
let req = Request::post("http://example.com/test")
.header(CONTENT_TYPE, mime::APPLICATION_WWW_FORM_URLENCODED.as_ref())
.body(Full::new(Bytes::from(body)))
.body(Body::from(body))
.unwrap();
assert_eq!(RawForm::from_request(req, &()).await.unwrap().0, body);
@ -109,7 +99,7 @@ mod tests {
#[crate::test]
async fn test_incorrect_content_type() {
let req = Request::post("http://example.com/test")
.body(Full::<Bytes>::from(Bytes::from("page=0&size=10")))
.body(Body::from("page=0&size=10"))
.unwrap();
assert!(matches!(

View file

@ -1,18 +1,8 @@
use super::{Extension, FromRequest, FromRequestParts};
use crate::{
body::{Body, Bytes, HttpBody},
BoxError, Error,
};
use crate::body::Body;
use async_trait::async_trait;
use futures_util::stream::Stream;
use http::{request::Parts, Request, Uri};
use std::{
convert::Infallible,
fmt,
pin::Pin,
task::{Context, Poll},
};
use sync_wrapper::SyncWrapper;
use std::convert::Infallible;
/// Extractor that gets the original request URI regardless of nesting.
///
@ -104,82 +94,6 @@ where
#[cfg(feature = "original-uri")]
axum_core::__impl_deref!(OriginalUri: Uri);
/// Extractor that extracts the request body as a [`Stream`].
///
/// Since extracting the request body requires consuming it, the `BodyStream` extractor must be
/// *last* if there are multiple extractors in a handler.
/// See ["the order of extractors"][order-of-extractors]
///
/// [order-of-extractors]: crate::extract#the-order-of-extractors
///
/// # Example
///
/// ```rust,no_run
/// use axum::{
/// extract::BodyStream,
/// routing::get,
/// Router,
/// };
/// use futures_util::StreamExt;
///
/// async fn handler(mut stream: BodyStream) {
/// while let Some(chunk) = stream.next().await {
/// // ...
/// }
/// }
///
/// let app = Router::new().route("/users", get(handler));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
///
/// [`Stream`]: https://docs.rs/futures/latest/futures/stream/trait.Stream.html
/// [`body::Body`]: crate::body::Body
pub struct BodyStream(
SyncWrapper<Pin<Box<dyn HttpBody<Data = Bytes, Error = Error> + Send + 'static>>>,
);
impl Stream for BodyStream {
type Item = Result<Bytes, Error>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
Pin::new(self.0.get_mut()).poll_data(cx)
}
}
#[async_trait]
impl<S, B> FromRequest<S, B> for BodyStream
where
B: HttpBody + Send + 'static,
B::Data: Into<Bytes>,
B::Error: Into<BoxError>,
S: Send + Sync,
{
type Rejection = Infallible;
async fn from_request(req: Request<B>, _state: &S) -> Result<Self, Self::Rejection> {
let body = req
.into_body()
.map_data(Into::into)
.map_err(|err| Error::new(err.into()));
let stream = BodyStream(SyncWrapper::new(Box::pin(body)));
Ok(stream)
}
}
impl fmt::Debug for BodyStream {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("BodyStream").finish()
}
}
#[test]
fn body_stream_traits() {
crate::test_helpers::assert_send::<BodyStream>();
crate::test_helpers::assert_sync::<BodyStream>();
}
/// Extractor that extracts the raw request body.
///
/// Since extracting the raw request body requires consuming it, the `RawBody` extractor must be
@ -208,24 +122,21 @@ fn body_stream_traits() {
/// ```
///
/// [`body::Body`]: crate::body::Body
#[derive(Debug, Default, Clone)]
pub struct RawBody<B = Body>(pub B);
#[derive(Debug, Default)]
pub struct RawBody(pub Body);
#[async_trait]
impl<S, B> FromRequest<S, B> for RawBody<B>
impl<S> FromRequest<S> for RawBody
where
B: Send,
S: Send + Sync,
{
type Rejection = Infallible;
async fn from_request(req: Request<B>, _state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(req: Request<Body>, _state: &S) -> Result<Self, Self::Rejection> {
Ok(Self(req.into_body()))
}
}
axum_core::__impl_deref!(RawBody);
#[cfg(test)]
mod tests {
use crate::{extract::Extension, routing::get, test_helpers::*, Router};

View file

@ -1,7 +1,6 @@
use crate::body::HttpBody;
use crate::extract::{rejection::*, FromRequest, RawForm};
use crate::BoxError;
use async_trait::async_trait;
use axum_core::body::Body;
use axum_core::response::{IntoResponse, Response};
use axum_core::RequestExt;
use http::header::CONTENT_TYPE;
@ -64,17 +63,14 @@ use serde::Serialize;
pub struct Form<T>(pub T);
#[async_trait]
impl<T, S, B> FromRequest<S, B> for Form<T>
impl<T, S> FromRequest<S> for Form<T>
where
T: DeserializeOwned,
B: HttpBody + Send + 'static,
B::Data: Send,
B::Error: Into<BoxError>,
S: Send + Sync,
{
type Rejection = FormRejection;
async fn from_request(req: Request<B>, _state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(req: Request<Body>, _state: &S) -> Result<Self, Self::Rejection> {
let is_get_or_head =
req.method() == http::Method::GET || req.method() == http::Method::HEAD;
@ -120,12 +116,11 @@ axum_core::__impl_deref!(Form);
mod tests {
use super::*;
use crate::{
body::{Empty, Full},
body::Body,
routing::{on, MethodFilter},
test_helpers::TestClient,
Router,
};
use bytes::Bytes;
use http::{header::CONTENT_TYPE, Method, Request};
use mime::APPLICATION_WWW_FORM_URLENCODED;
use serde::{Deserialize, Serialize};
@ -140,7 +135,7 @@ mod tests {
async fn check_query<T: DeserializeOwned + PartialEq + Debug>(uri: impl AsRef<str>, value: T) {
let req = Request::builder()
.uri(uri.as_ref())
.body(Empty::<Bytes>::new())
.body(Body::empty())
.unwrap();
assert_eq!(Form::<T>::from_request(req, &()).await.unwrap().0, value);
}
@ -150,9 +145,7 @@ mod tests {
.uri("http://example.com/test")
.method(Method::POST)
.header(CONTENT_TYPE, APPLICATION_WWW_FORM_URLENCODED.as_ref())
.body(Full::<Bytes>::new(
serde_urlencoded::to_string(&value).unwrap().into(),
))
.body(Body::from(serde_urlencoded::to_string(&value).unwrap()))
.unwrap();
assert_eq!(Form::<T>::from_request(req, &()).await.unwrap().0, value);
}
@ -214,13 +207,12 @@ mod tests {
.uri("http://example.com/test")
.method(Method::POST)
.header(CONTENT_TYPE, mime::APPLICATION_JSON.as_ref())
.body(Full::<Bytes>::new(
.body(Body::from(
serde_urlencoded::to_string(&Pagination {
size: Some(10),
page: None,
})
.unwrap()
.into(),
.unwrap(),
))
.unwrap();
assert!(matches!(

View file

@ -1,5 +1,6 @@
//! Handler future types.
use crate::body::Body;
use crate::response::Response;
use futures_util::future::Map;
use http::Request;
@ -19,29 +20,29 @@ opaque_future! {
pin_project! {
/// The response future for [`Layered`](super::Layered).
pub struct LayeredFuture<B, S>
pub struct LayeredFuture<S>
where
S: Service<Request<B>>,
S: Service<Request<Body>>,
{
#[pin]
inner: Map<Oneshot<S, Request<B>>, fn(Result<S::Response, S::Error>) -> Response>,
inner: Map<Oneshot<S, Request<Body>>, fn(Result<S::Response, S::Error>) -> Response>,
}
}
impl<B, S> LayeredFuture<B, S>
impl<S> LayeredFuture<S>
where
S: Service<Request<B>>,
S: Service<Request<Body>>,
{
pub(super) fn new(
inner: Map<Oneshot<S, Request<B>>, fn(Result<S::Response, S::Error>) -> Response>,
inner: Map<Oneshot<S, Request<Body>>, fn(Result<S::Response, S::Error>) -> Response>,
) -> Self {
Self { inner }
}
}
impl<B, S> Future for LayeredFuture<B, S>
impl<S> Future for LayeredFuture<S>
where
S: Service<Request<B>>,
S: Service<Request<Body>>,
{
type Output = Response;

View file

@ -37,11 +37,11 @@
#[cfg(feature = "tokio")]
use crate::extract::connect_info::IntoMakeServiceWithConnectInfo;
use crate::{
body::Body,
extract::{FromRequest, FromRequestParts},
response::{IntoResponse, Response},
routing::IntoMakeService,
};
use axum_core::body::Body;
use http::Request;
use std::{convert::Infallible, fmt, future::Future, marker::PhantomData, pin::Pin};
use tower::ServiceExt;
@ -99,12 +99,12 @@ pub use self::service::HandlerService;
note = "Consider using `#[axum::debug_handler]` to improve the error message"
)
)]
pub trait Handler<T, S, B = Body>: Clone + Send + Sized + 'static {
pub trait Handler<T, S>: Clone + Send + Sized + 'static {
/// The type of future calling this handler returns.
type Future: Future<Output = Response> + Send + 'static;
/// Call the handler with the given request.
fn call(self, req: Request<B>, state: S) -> Self::Future;
fn call(self, req: Request<Body>, state: S) -> Self::Future;
/// Apply a [`tower::Layer`] to the handler.
///
@ -142,10 +142,10 @@ pub trait Handler<T, S, B = Body>: Clone + Send + Sized + 'static {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
fn layer<L, NewReqBody>(self, layer: L) -> Layered<L, Self, T, S, B, NewReqBody>
fn layer<L>(self, layer: L) -> Layered<L, Self, T, S>
where
L: Layer<HandlerService<Self, T, S, B>> + Clone,
L::Service: Service<Request<NewReqBody>>,
L: Layer<HandlerService<Self, T, S>> + Clone,
L::Service: Service<Request<Body>>,
{
Layered {
layer,
@ -155,21 +155,20 @@ pub trait Handler<T, S, B = Body>: Clone + Send + Sized + 'static {
}
/// Convert the handler into a [`Service`] by providing the state
fn with_state(self, state: S) -> HandlerService<Self, T, S, B> {
fn with_state(self, state: S) -> HandlerService<Self, T, S> {
HandlerService::new(self, state)
}
}
impl<F, Fut, Res, S, B> Handler<((),), S, B> for F
impl<F, Fut, Res, S> Handler<((),), S> for F
where
F: FnOnce() -> Fut + Clone + Send + 'static,
Fut: Future<Output = Res> + Send,
Res: IntoResponse,
B: Send + 'static,
{
type Future = Pin<Box<dyn Future<Output = Response> + Send>>;
fn call(self, _req: Request<B>, _state: S) -> Self::Future {
fn call(self, _req: Request<Body>, _state: S) -> Self::Future {
Box::pin(async move { self().await.into_response() })
}
}
@ -179,19 +178,18 @@ macro_rules! impl_handler {
[$($ty:ident),*], $last:ident
) => {
#[allow(non_snake_case, unused_mut)]
impl<F, Fut, S, B, Res, M, $($ty,)* $last> Handler<(M, $($ty,)* $last,), S, B> for F
impl<F, Fut, S, Res, M, $($ty,)* $last> Handler<(M, $($ty,)* $last,), S> for F
where
F: FnOnce($($ty,)* $last,) -> Fut + Clone + Send + 'static,
Fut: Future<Output = Res> + Send,
B: Send + 'static,
S: Send + Sync + 'static,
Res: IntoResponse,
$( $ty: FromRequestParts<S> + Send, )*
$last: FromRequest<S, B, M> + Send,
$last: FromRequest<S, M> + Send,
{
type Future = Pin<Box<dyn Future<Output = Response> + Send>>;
fn call(self, req: Request<B>, state: S) -> Self::Future {
fn call(self, req: Request<Body>, state: S) -> Self::Future {
Box::pin(async move {
let (mut parts, body) = req.into_parts();
let state = &state;
@ -224,13 +222,13 @@ all_the_tuples!(impl_handler);
/// A [`Service`] created from a [`Handler`] by applying a Tower middleware.
///
/// Created with [`Handler::layer`]. See that method for more details.
pub struct Layered<L, H, T, S, B, B2> {
pub struct Layered<L, H, T, S> {
layer: L,
handler: H,
_marker: PhantomData<fn() -> (T, S, B, B2)>,
_marker: PhantomData<fn() -> (T, S)>,
}
impl<L, H, T, S, B, B2> fmt::Debug for Layered<L, H, T, S, B, B2>
impl<L, H, T, S> fmt::Debug for Layered<L, H, T, S>
where
L: fmt::Debug,
{
@ -241,7 +239,7 @@ where
}
}
impl<L, H, T, S, B, B2> Clone for Layered<L, H, T, S, B, B2>
impl<L, H, T, S> Clone for Layered<L, H, T, S>
where
L: Clone,
H: Clone,
@ -255,21 +253,19 @@ where
}
}
impl<H, S, T, L, B, B2> Handler<T, S, B2> for Layered<L, H, T, S, B, B2>
impl<H, S, T, L> Handler<T, S> for Layered<L, H, T, S>
where
L: Layer<HandlerService<H, T, S, B>> + Clone + Send + 'static,
H: Handler<T, S, B>,
L::Service: Service<Request<B2>, Error = Infallible> + Clone + Send + 'static,
<L::Service as Service<Request<B2>>>::Response: IntoResponse,
<L::Service as Service<Request<B2>>>::Future: Send,
L: Layer<HandlerService<H, T, S>> + Clone + Send + 'static,
H: Handler<T, S>,
L::Service: Service<Request<Body>, Error = Infallible> + Clone + Send + 'static,
<L::Service as Service<Request<Body>>>::Response: IntoResponse,
<L::Service as Service<Request<Body>>>::Future: Send,
T: 'static,
S: 'static,
B: Send + 'static,
B2: Send + 'static,
{
type Future = future::LayeredFuture<B2, L::Service>;
type Future = future::LayeredFuture<L::Service>;
fn call(self, req: Request<B2>, state: S) -> Self::Future {
fn call(self, req: Request<Body>, state: S) -> Self::Future {
use futures_util::future::{FutureExt, Map};
let svc = self.handler.with_state(state);
@ -279,8 +275,8 @@ where
_,
fn(
Result<
<L::Service as Service<Request<B2>>>::Response,
<L::Service as Service<Request<B2>>>::Error,
<L::Service as Service<Request<Body>>>::Response,
<L::Service as Service<Request<Body>>>::Error,
>,
) -> _,
> = svc.oneshot(req).map(|result| match result {
@ -297,16 +293,16 @@ where
/// This provides convenience methods to convert the [`Handler`] into a [`Service`] or [`MakeService`].
///
/// [`MakeService`]: tower::make::MakeService
pub trait HandlerWithoutStateExt<T, B>: Handler<T, (), B> {
pub trait HandlerWithoutStateExt<T>: Handler<T, ()> {
/// Convert the handler into a [`Service`] and no state.
fn into_service(self) -> HandlerService<Self, T, (), B>;
fn into_service(self) -> HandlerService<Self, T, ()>;
/// Convert the handler into a [`MakeService`] and no state.
///
/// See [`HandlerService::into_make_service`] for more details.
///
/// [`MakeService`]: tower::make::MakeService
fn into_make_service(self) -> IntoMakeService<HandlerService<Self, T, (), B>>;
fn into_make_service(self) -> IntoMakeService<HandlerService<Self, T, ()>>;
/// Convert the handler into a [`MakeService`] which stores information
/// about the incoming connection and has no state.
@ -317,25 +313,25 @@ pub trait HandlerWithoutStateExt<T, B>: Handler<T, (), B> {
#[cfg(feature = "tokio")]
fn into_make_service_with_connect_info<C>(
self,
) -> IntoMakeServiceWithConnectInfo<HandlerService<Self, T, (), B>, C>;
) -> IntoMakeServiceWithConnectInfo<HandlerService<Self, T, ()>, C>;
}
impl<H, T, B> HandlerWithoutStateExt<T, B> for H
impl<H, T> HandlerWithoutStateExt<T> for H
where
H: Handler<T, (), B>,
H: Handler<T, ()>,
{
fn into_service(self) -> HandlerService<Self, T, (), B> {
fn into_service(self) -> HandlerService<Self, T, ()> {
self.with_state(())
}
fn into_make_service(self) -> IntoMakeService<HandlerService<Self, T, (), B>> {
fn into_make_service(self) -> IntoMakeService<HandlerService<Self, T, ()>> {
self.into_service().into_make_service()
}
#[cfg(feature = "tokio")]
fn into_make_service_with_connect_info<C>(
self,
) -> IntoMakeServiceWithConnectInfo<HandlerService<Self, T, (), B>, C> {
) -> IntoMakeServiceWithConnectInfo<HandlerService<Self, T, ()>, C> {
self.into_service().into_make_service_with_connect_info()
}
}

View file

@ -1,8 +1,10 @@
use super::Handler;
use crate::body::{Body, Bytes, HttpBody};
#[cfg(feature = "tokio")]
use crate::extract::connect_info::IntoMakeServiceWithConnectInfo;
use crate::response::Response;
use crate::routing::IntoMakeService;
use crate::BoxError;
use http::Request;
use std::{
convert::Infallible,
@ -17,13 +19,13 @@ use tower_service::Service;
/// Created with [`Handler::with_state`] or [`HandlerWithoutStateExt::into_service`].
///
/// [`HandlerWithoutStateExt::into_service`]: super::HandlerWithoutStateExt::into_service
pub struct HandlerService<H, T, S, B> {
pub struct HandlerService<H, T, S> {
handler: H,
state: S,
_marker: PhantomData<fn() -> (T, B)>,
_marker: PhantomData<fn() -> T>,
}
impl<H, T, S, B> HandlerService<H, T, S, B> {
impl<H, T, S> HandlerService<H, T, S> {
/// Get a reference to the state.
pub fn state(&self) -> &S {
&self.state
@ -61,7 +63,7 @@ impl<H, T, S, B> HandlerService<H, T, S, B> {
/// ```
///
/// [`MakeService`]: tower::make::MakeService
pub fn into_make_service(self) -> IntoMakeService<HandlerService<H, T, S, B>> {
pub fn into_make_service(self) -> IntoMakeService<HandlerService<H, T, S>> {
IntoMakeService::new(self)
}
@ -104,7 +106,7 @@ impl<H, T, S, B> HandlerService<H, T, S, B> {
#[cfg(feature = "tokio")]
pub fn into_make_service_with_connect_info<C>(
self,
) -> IntoMakeServiceWithConnectInfo<HandlerService<H, T, S, B>, C> {
) -> IntoMakeServiceWithConnectInfo<HandlerService<H, T, S>, C> {
IntoMakeServiceWithConnectInfo::new(self)
}
}
@ -112,11 +114,11 @@ impl<H, T, S, B> HandlerService<H, T, S, B> {
#[test]
fn traits() {
use crate::test_helpers::*;
assert_send::<HandlerService<(), NotSendSync, (), NotSendSync>>();
assert_sync::<HandlerService<(), NotSendSync, (), NotSendSync>>();
assert_send::<HandlerService<(), NotSendSync, ()>>();
assert_sync::<HandlerService<(), NotSendSync, ()>>();
}
impl<H, T, S, B> HandlerService<H, T, S, B> {
impl<H, T, S> HandlerService<H, T, S> {
pub(super) fn new(handler: H, state: S) -> Self {
Self {
handler,
@ -126,13 +128,13 @@ impl<H, T, S, B> HandlerService<H, T, S, B> {
}
}
impl<H, T, S, B> fmt::Debug for HandlerService<H, T, S, B> {
impl<H, T, S> fmt::Debug for HandlerService<H, T, S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("IntoService").finish_non_exhaustive()
}
}
impl<H, T, S, B> Clone for HandlerService<H, T, S, B>
impl<H, T, S> Clone for HandlerService<H, T, S>
where
H: Clone,
S: Clone,
@ -146,10 +148,11 @@ where
}
}
impl<H, T, S, B> Service<Request<B>> for HandlerService<H, T, S, B>
impl<H, T, S, B> Service<Request<B>> for HandlerService<H, T, S>
where
H: Handler<T, S, B> + Clone + Send + 'static,
B: Send + 'static,
H: Handler<T, S> + Clone + Send + 'static,
B: HttpBody<Data = Bytes> + Send + 'static,
B::Error: Into<BoxError>,
S: Clone + Send + Sync,
{
type Response = Response;
@ -167,6 +170,8 @@ where
fn call(&mut self, req: Request<B>) -> Self::Future {
use futures_util::future::FutureExt;
let req = req.map(Body::new);
let handler = self.handler.clone();
let future = Handler::call(handler, req, self.state.clone());
let future = future.map(Ok as _);

View file

@ -1,11 +1,10 @@
use crate::{
body::{Bytes, HttpBody},
extract::{rejection::*, FromRequest},
BoxError,
};
use crate::extract::{rejection::*, FromRequest};
use async_trait::async_trait;
use axum_core::response::{IntoResponse, Response};
use bytes::{BufMut, BytesMut};
use axum_core::{
body::Body,
response::{IntoResponse, Response},
};
use bytes::{BufMut, Bytes, BytesMut};
use http::{
header::{self, HeaderMap, HeaderValue},
Request, StatusCode,
@ -100,17 +99,14 @@ use serde::{de::DeserializeOwned, Serialize};
pub struct Json<T>(pub T);
#[async_trait]
impl<T, S, B> FromRequest<S, B> for Json<T>
impl<T, S> FromRequest<S> for Json<T>
where
T: DeserializeOwned,
B: HttpBody + Send + 'static,
B::Data: Send,
B::Error: Into<BoxError>,
S: Send + Sync,
{
type Rejection = JsonRejection;
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(req: Request<Body>, state: &S) -> Result<Self, Self::Rejection> {
if json_content_type(req.headers()) {
let bytes = Bytes::from_request(req, state).await?;
let deserializer = &mut serde_json::Deserializer::from_slice(&bytes);

View file

@ -1,4 +1,6 @@
use crate::body::{Body, Bytes, HttpBody};
use crate::response::{IntoResponse, Response};
use crate::BoxError;
use axum_core::extract::{FromRequest, FromRequestParts};
use futures_util::future::BoxFuture;
use http::Request;
@ -247,7 +249,7 @@ macro_rules! impl_service {
where
F: FnMut($($ty,)* $last, Next<B>) -> Fut + Clone + Send + 'static,
$( $ty: FromRequestParts<S> + Send, )*
$last: FromRequest<S, B> + Send,
$last: FromRequest<S> + Send,
Fut: Future<Output = Out> + Send + 'static,
Out: IntoResponse + 'static,
I: Service<Request<B>, Error = Infallible>
@ -256,7 +258,8 @@ macro_rules! impl_service {
+ 'static,
I::Response: IntoResponse,
I::Future: Send + 'static,
B: Send + 'static,
B: HttpBody<Data = Bytes> + Send + 'static,
B::Error: Into<BoxError>,
S: Clone + Send + Sync + 'static,
{
type Response = Response;
@ -268,6 +271,8 @@ macro_rules! impl_service {
}
fn call(&mut self, req: Request<B>) -> Self::Future {
let req = req.map(Body::new);
let not_ready_inner = self.inner.clone();
let ready_inner = std::mem::replace(&mut self.inner, not_ready_inner);

View file

@ -1,4 +1,6 @@
use crate::body::{Body, Bytes, HttpBody};
use crate::response::{IntoResponse, Response};
use crate::BoxError;
use axum_core::extract::{FromRequest, FromRequestParts};
use futures_util::future::BoxFuture;
use http::Request;
@ -251,7 +253,7 @@ macro_rules! impl_service {
where
F: FnMut($($ty,)* $last) -> Fut + Clone + Send + 'static,
$( $ty: FromRequestParts<S> + Send, )*
$last: FromRequest<S, B> + Send,
$last: FromRequest<S> + Send,
Fut: Future + Send + 'static,
Fut::Output: IntoMapRequestResult<B> + Send + 'static,
I: Service<Request<B>, Error = Infallible>
@ -260,7 +262,8 @@ macro_rules! impl_service {
+ 'static,
I::Response: IntoResponse,
I::Future: Send + 'static,
B: Send + 'static,
B: HttpBody<Data = Bytes> + Send + 'static,
B::Error: Into<BoxError>,
S: Clone + Send + Sync + 'static,
{
type Response = Response;
@ -272,6 +275,8 @@ macro_rules! impl_service {
}
fn call(&mut self, req: Request<B>) -> Self::Future {
let req = req.map(Body::new);
let not_ready_inner = self.inner.clone();
let mut ready_inner = std::mem::replace(&mut self.inner, not_ready_inner);

View file

@ -67,7 +67,7 @@ impl<T> From<T> for Html<T> {
#[cfg(test)]
mod tests {
use crate::extract::Extension;
use crate::{body::Body, routing::get, Router};
use crate::{routing::get, Router};
use axum_core::response::IntoResponse;
use http::HeaderMap;
use http::{StatusCode, Uri};
@ -99,7 +99,7 @@ mod tests {
}
}
_ = Router::<(), Body>::new()
_ = Router::<()>::new()
.route("/", get(impl_trait_ok))
.route("/", get(impl_trait_err))
.route("/", get(impl_trait_both))
@ -209,7 +209,7 @@ mod tests {
)
}
_ = Router::<(), Body>::new()
_ = Router::<()>::new()
.route("/", get(status))
.route("/", get(status_headermap))
.route("/", get(status_header_array))

View file

@ -12,7 +12,7 @@ use crate::{
response::Response,
routing::{future::RouteFuture, Fallback, MethodFilter, Route},
};
use axum_core::response::IntoResponse;
use axum_core::{response::IntoResponse, BoxError};
use bytes::BytesMut;
use std::{
convert::Infallible,
@ -37,10 +37,10 @@ macro_rules! top_level_service_fn {
/// http::Request,
/// Router,
/// routing::get_service,
/// body::Body,
/// };
/// use http::Response;
/// use std::convert::Infallible;
/// use hyper::Body;
///
/// let service = tower::service_fn(|request: Request<Body>| async {
/// Ok::<_, Infallible>(Response::new(Body::empty()))
@ -78,12 +78,11 @@ macro_rules! top_level_service_fn {
$name:ident, $method:ident
) => {
$(#[$m])+
pub fn $name<T, S, B>(svc: T) -> MethodRouter<S, B, T::Error>
pub fn $name<T, S>(svc: T) -> MethodRouter<S, T::Error>
where
T: Service<Request<B>> + Clone + Send + 'static,
T: Service<Request<Body>> + Clone + Send + 'static,
T::Response: IntoResponse + 'static,
T::Future: Send + 'static,
B: HttpBody + Send + 'static,
S: Clone,
{
on_service(MethodFilter::$method, svc)
@ -140,10 +139,9 @@ macro_rules! top_level_handler_fn {
$name:ident, $method:ident
) => {
$(#[$m])+
pub fn $name<H, T, S, B>(handler: H) -> MethodRouter<S, B, Infallible>
pub fn $name<H, T, S>(handler: H) -> MethodRouter<S, Infallible>
where
H: Handler<T, S, B>,
B: HttpBody + Send + 'static,
H: Handler<T, S>,
T: 'static,
S: Clone + Send + Sync + 'static,
{
@ -166,10 +164,10 @@ macro_rules! chained_service_fn {
/// http::Request,
/// Router,
/// routing::post_service,
/// body::Body,
/// };
/// use http::Response;
/// use std::convert::Infallible;
/// use hyper::Body;
///
/// let service = tower::service_fn(|request: Request<Body>| async {
/// Ok::<_, Infallible>(Response::new(Body::empty()))
@ -215,7 +213,7 @@ macro_rules! chained_service_fn {
#[track_caller]
pub fn $name<T>(self, svc: T) -> Self
where
T: Service<Request<B>, Error = E>
T: Service<Request<Body>, Error = E>
+ Clone
+ Send
+ 'static,
@ -279,7 +277,7 @@ macro_rules! chained_handler_fn {
#[track_caller]
pub fn $name<H, T>(self, handler: H) -> Self
where
H: Handler<T, S, B>,
H: Handler<T, S>,
T: 'static,
S: Send + Sync + 'static,
{
@ -306,11 +304,11 @@ top_level_service_fn!(trace_service, TRACE);
/// http::Request,
/// routing::on,
/// Router,
/// body::Body,
/// routing::{MethodFilter, on_service},
/// };
/// use http::Response;
/// use std::convert::Infallible;
/// use hyper::Body;
///
/// let service = tower::service_fn(|request: Request<Body>| async {
/// Ok::<_, Infallible>(Response::new(Body::empty()))
@ -322,12 +320,11 @@ top_level_service_fn!(trace_service, TRACE);
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
pub fn on_service<T, S, B>(filter: MethodFilter, svc: T) -> MethodRouter<S, B, T::Error>
pub fn on_service<T, S>(filter: MethodFilter, svc: T) -> MethodRouter<S, T::Error>
where
T: Service<Request<B>> + Clone + Send + 'static,
T: Service<Request<Body>> + Clone + Send + 'static,
T::Response: IntoResponse + 'static,
T::Future: Send + 'static,
B: HttpBody + Send + 'static,
S: Clone,
{
MethodRouter::new().on_service(filter, svc)
@ -342,10 +339,10 @@ where
/// http::Request,
/// Router,
/// routing::any_service,
/// body::Body,
/// };
/// use http::Response;
/// use std::convert::Infallible;
/// use hyper::Body;
///
/// let service = tower::service_fn(|request: Request<Body>| async {
/// Ok::<_, Infallible>(Response::new(Body::empty()))
@ -365,10 +362,10 @@ where
/// http::Request,
/// Router,
/// routing::any_service,
/// body::Body,
/// };
/// use http::Response;
/// use std::convert::Infallible;
/// use hyper::Body;
///
/// let service = tower::service_fn(|request: Request<Body>| async {
/// # Ok::<_, Infallible>(Response::new(Body::empty()))
@ -386,12 +383,11 @@ where
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
pub fn any_service<T, S, B>(svc: T) -> MethodRouter<S, B, T::Error>
pub fn any_service<T, S>(svc: T) -> MethodRouter<S, T::Error>
where
T: Service<Request<B>> + Clone + Send + 'static,
T: Service<Request<Body>> + Clone + Send + 'static,
T::Response: IntoResponse + 'static,
T::Future: Send + 'static,
B: HttpBody + Send + 'static,
S: Clone,
{
MethodRouter::new()
@ -427,10 +423,9 @@ top_level_handler_fn!(trace, TRACE);
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
pub fn on<H, T, S, B>(filter: MethodFilter, handler: H) -> MethodRouter<S, B, Infallible>
pub fn on<H, T, S>(filter: MethodFilter, handler: H) -> MethodRouter<S, Infallible>
where
H: Handler<T, S, B>,
B: HttpBody + Send + 'static,
H: Handler<T, S>,
T: 'static,
S: Clone + Send + Sync + 'static,
{
@ -474,10 +469,9 @@ where
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
pub fn any<H, T, S, B>(handler: H) -> MethodRouter<S, B, Infallible>
pub fn any<H, T, S>(handler: H) -> MethodRouter<S, Infallible>
where
H: Handler<T, S, B>,
B: HttpBody + Send + 'static,
H: Handler<T, S>,
T: 'static,
S: Clone + Send + Sync + 'static,
{
@ -514,16 +508,16 @@ where
/// {}
/// ```
#[must_use]
pub struct MethodRouter<S = (), B = Body, E = Infallible> {
get: MethodEndpoint<S, B, E>,
head: MethodEndpoint<S, B, E>,
delete: MethodEndpoint<S, B, E>,
options: MethodEndpoint<S, B, E>,
patch: MethodEndpoint<S, B, E>,
post: MethodEndpoint<S, B, E>,
put: MethodEndpoint<S, B, E>,
trace: MethodEndpoint<S, B, E>,
fallback: Fallback<S, B, E>,
pub struct MethodRouter<S = (), E = Infallible> {
get: MethodEndpoint<S, E>,
head: MethodEndpoint<S, E>,
delete: MethodEndpoint<S, E>,
options: MethodEndpoint<S, E>,
patch: MethodEndpoint<S, E>,
post: MethodEndpoint<S, E>,
put: MethodEndpoint<S, E>,
trace: MethodEndpoint<S, E>,
fallback: Fallback<S, E>,
allow_header: AllowHeader,
}
@ -553,7 +547,7 @@ impl AllowHeader {
}
}
impl<S, B, E> fmt::Debug for MethodRouter<S, B, E> {
impl<S, E> fmt::Debug for MethodRouter<S, E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MethodRouter")
.field("get", &self.get)
@ -570,9 +564,8 @@ impl<S, B, E> fmt::Debug for MethodRouter<S, B, E> {
}
}
impl<S, B> MethodRouter<S, B, Infallible>
impl<S> MethodRouter<S, Infallible>
where
B: HttpBody + Send + 'static,
S: Clone,
{
/// Chain an additional handler that will accept requests matching the given
@ -601,7 +594,7 @@ where
#[track_caller]
pub fn on<H, T>(self, filter: MethodFilter, handler: H) -> Self
where
H: Handler<T, S, B>,
H: Handler<T, S>,
T: 'static,
S: Send + Sync + 'static,
{
@ -623,7 +616,7 @@ where
/// Add a fallback [`Handler`] to the router.
pub fn fallback<H, T>(mut self, handler: H) -> Self
where
H: Handler<T, S, B>,
H: Handler<T, S>,
T: 'static,
S: Send + Sync + 'static,
{
@ -632,10 +625,7 @@ where
}
}
impl<B> MethodRouter<(), B, Infallible>
where
B: HttpBody + Send + 'static,
{
impl MethodRouter<(), Infallible> {
/// Convert the handler into a [`MakeService`].
///
/// This allows you to serve a single handler if you don't need any routing:
@ -706,15 +696,14 @@ where
}
}
impl<S, B, E> MethodRouter<S, B, E>
impl<S, E> MethodRouter<S, E>
where
B: HttpBody + Send + 'static,
S: Clone,
{
/// Create a default `MethodRouter` that will respond with `405 Method Not Allowed` to all
/// requests.
pub fn new() -> Self {
let fallback = Route::new(service_fn(|_: Request<B>| async {
let fallback = Route::new(service_fn(|_: Request<Body>| async {
Ok(StatusCode::METHOD_NOT_ALLOWED.into_response())
}));
@ -733,7 +722,7 @@ where
}
/// Provide the state for the router.
pub fn with_state<S2>(self, state: S) -> MethodRouter<S2, B, E> {
pub fn with_state<S2>(self, state: S) -> MethodRouter<S2, E> {
MethodRouter {
get: self.get.with_state(&state),
head: self.head.with_state(&state),
@ -758,10 +747,10 @@ where
/// http::Request,
/// Router,
/// routing::{MethodFilter, on_service},
/// body::Body,
/// };
/// use http::Response;
/// use std::convert::Infallible;
/// use hyper::Body;
///
/// let service = tower::service_fn(|request: Request<Body>| async {
/// Ok::<_, Infallible>(Response::new(Body::empty()))
@ -776,7 +765,7 @@ where
#[track_caller]
pub fn on_service<T>(self, filter: MethodFilter, svc: T) -> Self
where
T: Service<Request<B>, Error = E> + Clone + Send + 'static,
T: Service<Request<Body>, Error = E> + Clone + Send + 'static,
T::Response: IntoResponse + 'static,
T::Future: Send + 'static,
{
@ -784,19 +773,19 @@ where
}
#[track_caller]
fn on_endpoint(mut self, filter: MethodFilter, endpoint: MethodEndpoint<S, B, E>) -> Self {
fn on_endpoint(mut self, filter: MethodFilter, endpoint: MethodEndpoint<S, E>) -> Self {
// written as a separate function to generate less IR
#[track_caller]
fn set_endpoint<S, B, E>(
fn set_endpoint<S, E>(
method_name: &str,
out: &mut MethodEndpoint<S, B, E>,
endpoint: &MethodEndpoint<S, B, E>,
out: &mut MethodEndpoint<S, E>,
endpoint: &MethodEndpoint<S, E>,
endpoint_filter: MethodFilter,
filter: MethodFilter,
allow_header: &mut AllowHeader,
methods: &[&'static str],
) where
MethodEndpoint<S, B, E>: Clone,
MethodEndpoint<S, E>: Clone,
S: Clone,
{
if endpoint_filter.contains(filter) {
@ -908,7 +897,7 @@ where
#[doc = include_str!("../docs/method_routing/fallback.md")]
pub fn fallback_service<T>(mut self, svc: T) -> Self
where
T: Service<Request<B>, Error = E> + Clone + Send + 'static,
T: Service<Request<Body>, Error = E> + Clone + Send + 'static,
T::Response: IntoResponse + 'static,
T::Future: Send + 'static,
{
@ -917,19 +906,18 @@ where
}
#[doc = include_str!("../docs/method_routing/layer.md")]
pub fn layer<L, NewReqBody, NewError>(self, layer: L) -> MethodRouter<S, NewReqBody, NewError>
pub fn layer<L, NewError>(self, layer: L) -> MethodRouter<S, NewError>
where
L: Layer<Route<B, E>> + Clone + Send + 'static,
L::Service: Service<Request<NewReqBody>> + Clone + Send + 'static,
<L::Service as Service<Request<NewReqBody>>>::Response: IntoResponse + 'static,
<L::Service as Service<Request<NewReqBody>>>::Error: Into<NewError> + 'static,
<L::Service as Service<Request<NewReqBody>>>::Future: Send + 'static,
L: Layer<Route<E>> + Clone + Send + 'static,
L::Service: Service<Request<Body>> + Clone + Send + 'static,
<L::Service as Service<Request<Body>>>::Response: IntoResponse + 'static,
<L::Service as Service<Request<Body>>>::Error: Into<NewError> + 'static,
<L::Service as Service<Request<Body>>>::Future: Send + 'static,
E: 'static,
S: 'static,
NewReqBody: HttpBody + 'static,
NewError: 'static,
{
let layer_fn = move |route: Route<B, E>| route.layer(layer.clone());
let layer_fn = move |route: Route<E>| route.layer(layer.clone());
MethodRouter {
get: self.get.map(layer_fn.clone()),
@ -947,12 +935,12 @@ where
#[doc = include_str!("../docs/method_routing/route_layer.md")]
#[track_caller]
pub fn route_layer<L>(mut self, layer: L) -> MethodRouter<S, B, E>
pub fn route_layer<L>(mut self, layer: L) -> MethodRouter<S, E>
where
L: Layer<Route<B, E>> + Clone + Send + 'static,
L::Service: Service<Request<B>, Error = E> + Clone + Send + 'static,
<L::Service as Service<Request<B>>>::Response: IntoResponse + 'static,
<L::Service as Service<Request<B>>>::Future: Send + 'static,
L: Layer<Route<E>> + Clone + Send + 'static,
L::Service: Service<Request<Body>, Error = E> + Clone + Send + 'static,
<L::Service as Service<Request<Body>>>::Response: IntoResponse + 'static,
<L::Service as Service<Request<Body>>>::Future: Send + 'static,
E: 'static,
S: 'static,
{
@ -990,19 +978,15 @@ where
}
#[track_caller]
pub(crate) fn merge_for_path(
mut self,
path: Option<&str>,
other: MethodRouter<S, B, E>,
) -> Self {
pub(crate) fn merge_for_path(mut self, path: Option<&str>, other: MethodRouter<S, E>) -> Self {
// written using inner functions to generate less IR
#[track_caller]
fn merge_inner<S, B, E>(
fn merge_inner<S, E>(
path: Option<&str>,
name: &str,
first: MethodEndpoint<S, B, E>,
second: MethodEndpoint<S, B, E>,
) -> MethodEndpoint<S, B, E> {
first: MethodEndpoint<S, E>,
second: MethodEndpoint<S, E>,
) -> MethodEndpoint<S, E> {
match (first, second) {
(MethodEndpoint::None, MethodEndpoint::None) => MethodEndpoint::None,
(pick, MethodEndpoint::None) | (MethodEndpoint::None, pick) => pick,
@ -1042,22 +1026,21 @@ where
#[doc = include_str!("../docs/method_routing/merge.md")]
#[track_caller]
pub fn merge(self, other: MethodRouter<S, B, E>) -> Self {
pub fn merge(self, other: MethodRouter<S, E>) -> Self {
self.merge_for_path(None, other)
}
/// Apply a [`HandleErrorLayer`].
///
/// This is a convenience method for doing `self.layer(HandleErrorLayer::new(f))`.
pub fn handle_error<F, T>(self, f: F) -> MethodRouter<S, B, Infallible>
pub fn handle_error<F, T>(self, f: F) -> MethodRouter<S, Infallible>
where
F: Clone + Send + Sync + 'static,
HandleError<Route<B, E>, F, T>: Service<Request<B>, Error = Infallible>,
<HandleError<Route<B, E>, F, T> as Service<Request<B>>>::Future: Send,
<HandleError<Route<B, E>, F, T> as Service<Request<B>>>::Response: IntoResponse + Send,
HandleError<Route<E>, F, T>: Service<Request<Body>, Error = Infallible>,
<HandleError<Route<E>, F, T> as Service<Request<Body>>>::Future: Send,
<HandleError<Route<E>, F, T> as Service<Request<Body>>>::Response: IntoResponse + Send,
T: 'static,
E: 'static,
B: 'static,
S: 'static,
{
self.layer(HandleErrorLayer::new(f))
@ -1068,7 +1051,7 @@ where
self
}
pub(crate) fn call_with_state(&mut self, req: Request<B>, state: S) -> RouteFuture<B, E> {
pub(crate) fn call_with_state(&mut self, req: Request<Body>, state: S) -> RouteFuture<E> {
macro_rules! call {
(
$req:expr,
@ -1157,7 +1140,7 @@ fn append_allow_header(allow_header: &mut AllowHeader, method: &'static str) {
}
}
impl<S, B, E> Clone for MethodRouter<S, B, E> {
impl<S, E> Clone for MethodRouter<S, E> {
fn clone(&self) -> Self {
Self {
get: self.get.clone(),
@ -1174,9 +1157,8 @@ impl<S, B, E> Clone for MethodRouter<S, B, E> {
}
}
impl<S, B, E> Default for MethodRouter<S, B, E>
impl<S, E> Default for MethodRouter<S, E>
where
B: HttpBody + Send + 'static,
S: Clone,
{
fn default() -> Self {
@ -1184,13 +1166,13 @@ where
}
}
enum MethodEndpoint<S, B, E> {
enum MethodEndpoint<S, E> {
None,
Route(Route<B, E>),
BoxedHandler(BoxedIntoRoute<S, B, E>),
Route(Route<E>),
BoxedHandler(BoxedIntoRoute<S, E>),
}
impl<S, B, E> MethodEndpoint<S, B, E>
impl<S, E> MethodEndpoint<S, E>
where
S: Clone,
{
@ -1202,13 +1184,11 @@ where
matches!(self, Self::None)
}
fn map<F, B2, E2>(self, f: F) -> MethodEndpoint<S, B2, E2>
fn map<F, E2>(self, f: F) -> MethodEndpoint<S, E2>
where
S: 'static,
B: 'static,
E: 'static,
F: FnOnce(Route<B, E>) -> Route<B2, E2> + Clone + Send + 'static,
B2: HttpBody + 'static,
F: FnOnce(Route<E>) -> Route<E2> + Clone + Send + 'static,
E2: 'static,
{
match self {
@ -1218,7 +1198,7 @@ where
}
}
fn with_state<S2>(self, state: &S) -> MethodEndpoint<S2, B, E> {
fn with_state<S2>(self, state: &S) -> MethodEndpoint<S2, E> {
match self {
MethodEndpoint::None => MethodEndpoint::None,
MethodEndpoint::Route(route) => MethodEndpoint::Route(route),
@ -1229,7 +1209,7 @@ where
}
}
impl<S, B, E> Clone for MethodEndpoint<S, B, E> {
impl<S, E> Clone for MethodEndpoint<S, E> {
fn clone(&self) -> Self {
match self {
Self::None => Self::None,
@ -1239,7 +1219,7 @@ impl<S, B, E> Clone for MethodEndpoint<S, B, E> {
}
}
impl<S, B, E> fmt::Debug for MethodEndpoint<S, B, E> {
impl<S, E> fmt::Debug for MethodEndpoint<S, E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::None => f.debug_tuple("None").finish(),
@ -1249,13 +1229,14 @@ impl<S, B, E> fmt::Debug for MethodEndpoint<S, B, E> {
}
}
impl<B, E> Service<Request<B>> for MethodRouter<(), B, E>
impl<B, E> Service<Request<B>> for MethodRouter<(), E>
where
B: HttpBody + Send + 'static,
B: HttpBody<Data = Bytes> + Send + 'static,
B::Error: Into<BoxError>,
{
type Response = Response;
type Error = E;
type Future = RouteFuture<B, E>;
type Future = RouteFuture<E>;
#[inline]
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
@ -1264,18 +1245,18 @@ where
#[inline]
fn call(&mut self, req: Request<B>) -> Self::Future {
let req = req.map(Body::new);
self.call_with_state(req, ())
}
}
impl<S, B> Handler<(), S, B> for MethodRouter<S, B>
impl<S> Handler<(), S> for MethodRouter<S>
where
S: Clone + 'static,
B: HttpBody + Send + 'static,
{
type Future = InfallibleRouteFuture<B>;
type Future = InfallibleRouteFuture;
fn call(mut self, req: Request<B>, state: S) -> Self::Future {
fn call(mut self, req: Request<Body>, state: S) -> Self::Future {
InfallibleRouteFuture::new(self.call_with_state(req, state))
}
}

View file

@ -14,7 +14,7 @@ use http::Request;
use std::{
convert::Infallible,
fmt,
task::{Context, Poll},
task::{Context, Poll}, marker::PhantomData,
};
use sync_wrapper::SyncWrapper;
use tower_layer::Layer;
@ -56,13 +56,13 @@ pub(crate) struct RouteId(u32);
/// The router type for composing handlers and services.
#[must_use]
pub struct Router<S = (), B = Body> {
path_router: PathRouter<S, B, false>,
fallback_router: PathRouter<S, B, true>,
pub struct Router<S = ()> {
path_router: PathRouter<S, false>,
fallback_router: PathRouter<S, true>,
default_fallback: bool,
}
impl<S, B> Clone for Router<S, B> {
impl<S> Clone for Router<S> {
fn clone(&self) -> Self {
Self {
path_router: self.path_router.clone(),
@ -72,9 +72,8 @@ impl<S, B> Clone for Router<S, B> {
}
}
impl<S, B> Default for Router<S, B>
impl<S> Default for Router<S>
where
B: HttpBody + Send + 'static,
S: Clone + Send + Sync + 'static,
{
fn default() -> Self {
@ -82,7 +81,7 @@ where
}
}
impl<S, B> fmt::Debug for Router<S, B> {
impl<S> fmt::Debug for Router<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Router")
.field("path_router", &self.path_router)
@ -96,9 +95,8 @@ pub(crate) const NEST_TAIL_PARAM: &str = "__private__axum_nest_tail_param";
pub(crate) const NEST_TAIL_PARAM_CAPTURE: &str = "/*__private__axum_nest_tail_param";
pub(crate) const FALLBACK_PARAM: &str = "__private__axum_fallback";
impl<S, B> Router<S, B>
impl<S> Router<S>
where
B: HttpBody + Send + 'static,
S: Clone + Send + Sync + 'static,
{
/// Create a new `Router`.
@ -118,7 +116,7 @@ where
#[doc = include_str!("../docs/routing/route.md")]
#[track_caller]
pub fn route(mut self, path: &str, method_router: MethodRouter<S, B>) -> Self {
pub fn route(mut self, path: &str, method_router: MethodRouter<S>) -> Self {
panic_on_err!(self.path_router.route(path, method_router));
self
}
@ -126,11 +124,11 @@ where
#[doc = include_str!("../docs/routing/route_service.md")]
pub fn route_service<T>(mut self, path: &str, service: T) -> Self
where
T: Service<Request<B>, Error = Infallible> + Clone + Send + 'static,
T: Service<Request<Body>, Error = Infallible> + Clone + Send + 'static,
T::Response: IntoResponse,
T::Future: Send + 'static,
{
let service = match try_downcast::<Router<S, B>, _>(service) {
let service = match try_downcast::<Router<S>, _>(service) {
Ok(_) => {
panic!(
"Invalid route: `Router::route_service` cannot be used with `Router`s. \
@ -146,7 +144,7 @@ where
#[doc = include_str!("../docs/routing/nest.md")]
#[track_caller]
pub fn nest(mut self, path: &str, router: Router<S, B>) -> Self {
pub fn nest(mut self, path: &str, router: Router<S>) -> Self {
let Router {
path_router,
fallback_router,
@ -166,7 +164,7 @@ where
#[track_caller]
pub fn nest_service<T>(mut self, path: &str, service: T) -> Self
where
T: Service<Request<B>, Error = Infallible> + Clone + Send + 'static,
T: Service<Request<Body>, Error = Infallible> + Clone + Send + 'static,
T::Response: IntoResponse,
T::Future: Send + 'static,
{
@ -178,7 +176,7 @@ where
#[track_caller]
pub fn merge<R>(mut self, other: R) -> Self
where
R: Into<Router<S, B>>,
R: Into<Router<S>>,
{
let Router {
path_router,
@ -212,14 +210,13 @@ where
}
#[doc = include_str!("../docs/routing/layer.md")]
pub fn layer<L, NewReqBody>(self, layer: L) -> Router<S, NewReqBody>
pub fn layer<L>(self, layer: L) -> Router<S>
where
L: Layer<Route<B>> + Clone + Send + 'static,
L::Service: Service<Request<NewReqBody>> + Clone + Send + 'static,
<L::Service as Service<Request<NewReqBody>>>::Response: IntoResponse + 'static,
<L::Service as Service<Request<NewReqBody>>>::Error: Into<Infallible> + 'static,
<L::Service as Service<Request<NewReqBody>>>::Future: Send + 'static,
NewReqBody: HttpBody + 'static,
L: Layer<Route> + Clone + Send + 'static,
L::Service: Service<Request<Body>> + Clone + Send + 'static,
<L::Service as Service<Request<Body>>>::Response: IntoResponse + 'static,
<L::Service as Service<Request<Body>>>::Error: Into<Infallible> + 'static,
<L::Service as Service<Request<Body>>>::Future: Send + 'static,
{
Router {
path_router: self.path_router.layer(layer.clone()),
@ -232,11 +229,11 @@ where
#[track_caller]
pub fn route_layer<L>(self, layer: L) -> Self
where
L: Layer<Route<B>> + Clone + Send + 'static,
L::Service: Service<Request<B>> + Clone + Send + 'static,
<L::Service as Service<Request<B>>>::Response: IntoResponse + 'static,
<L::Service as Service<Request<B>>>::Error: Into<Infallible> + 'static,
<L::Service as Service<Request<B>>>::Future: Send + 'static,
L: Layer<Route> + Clone + Send + 'static,
L::Service: Service<Request<Body>> + Clone + Send + 'static,
<L::Service as Service<Request<Body>>>::Response: IntoResponse + 'static,
<L::Service as Service<Request<Body>>>::Error: Into<Infallible> + 'static,
<L::Service as Service<Request<Body>>>::Future: Send + 'static,
{
Router {
path_router: self.path_router.route_layer(layer),
@ -249,7 +246,7 @@ where
#[doc = include_str!("../docs/routing/fallback.md")]
pub fn fallback<H, T>(self, handler: H) -> Self
where
H: Handler<T, S, B>,
H: Handler<T, S>,
T: 'static,
{
let endpoint = Endpoint::MethodRouter(any(handler));
@ -261,14 +258,14 @@ where
/// See [`Router::fallback`] for more details.
pub fn fallback_service<T>(self, service: T) -> Self
where
T: Service<Request<B>, Error = Infallible> + Clone + Send + 'static,
T: Service<Request<Body>, Error = Infallible> + Clone + Send + 'static,
T::Response: IntoResponse,
T::Future: Send + 'static,
{
self.fallback_endpoint(Endpoint::Route(Route::new(service)))
}
fn fallback_endpoint(mut self, endpoint: Endpoint<S, B>) -> Self {
fn fallback_endpoint(mut self, endpoint: Endpoint<S>) -> Self {
self.fallback_router.replace_endpoint("/", endpoint.clone());
self.fallback_router
.replace_endpoint(&format!("/*{FALLBACK_PARAM}"), endpoint);
@ -277,7 +274,7 @@ where
}
#[doc = include_str!("../docs/routing/with_state.md")]
pub fn with_state<S2>(self, state: S) -> Router<S2, B> {
pub fn with_state<S2>(self, state: S) -> Router<S2> {
Router {
path_router: self.path_router.with_state(state.clone()),
fallback_router: self.fallback_router.with_state(state),
@ -287,9 +284,9 @@ where
pub(crate) fn call_with_state(
&mut self,
mut req: Request<B>,
mut req: Request<Body>,
state: S,
) -> RouteFuture<B, Infallible> {
) -> RouteFuture<Infallible> {
// required for opaque routers to still inherit the fallback
// TODO(david): remove this feature in 0.7
if !self.default_fallback {
@ -303,7 +300,7 @@ where
Err((mut req, state)) => {
let super_fallback = req
.extensions_mut()
.remove::<SuperFallback<S, B>>()
.remove::<SuperFallback<S>>()
.map(|SuperFallback(path_router)| path_router.into_inner());
if let Some(mut super_fallback) = super_fallback {
@ -324,12 +321,81 @@ where
}
}
}
/// Convert the router into a borrowed [`Service`] with a fixed request body type, to aid type
/// inference.
///
/// In some cases when calling methods from [`tower::ServiceExt`] on a [`Router`] you might get
/// type inference errors along the lines of
///
/// ```not_rust
/// let response = router.ready().await?.call(request).await?;
/// ^^^^^ cannot infer type for type parameter `B`
/// ```
///
/// This happens because `Router` implements [`Service`] with `impl<B> Service<Request<B>> for Router<()>`.
///
/// For example:
///
/// ```compile_fail
/// use axum::{
/// Router,
/// routing::get,
/// http::Request,
/// body::Body,
/// };
/// use tower::{Service, ServiceExt};
///
/// # async fn async_main() -> Result<(), Box<dyn std::error::Error>> {
/// let mut router = Router::new().route("/", get(|| async {}));
/// let request = Request::new(Body::empty());
/// let response = router.ready().await?.call(request).await?;
/// # Ok(())
/// # }
/// ```
///
/// Calling `Router::as_service` fixes that:
///
/// ```
/// use axum::{
/// Router,
/// routing::get,
/// http::Request,
/// body::Body,
/// };
/// use tower::{Service, ServiceExt};
///
/// # async fn async_main() -> Result<(), Box<dyn std::error::Error>> {
/// let mut router = Router::new().route("/", get(|| async {}));
/// let request = Request::new(Body::empty());
/// let response = router.as_service().ready().await?.call(request).await?;
/// # Ok(())
/// # }
/// ```
///
/// This is mainly used when calling `Router` in tests. It shouldn't be necessary when running
/// the `Router` normally via [`Router::into_make_service`].
pub fn as_service<B>(&mut self) -> RouterAsService<'_, B, S> {
RouterAsService {
router: self,
_marker: PhantomData,
}
}
/// Convert the router into an owned [`Service`] with a fixed request body type, to aid type
/// inference.
///
/// This is the same as [`Router::as_service`] instead it returns an owned [`Service`]. See
/// that method for more details.
pub fn into_service<B>(self) -> RouterIntoService<B, S> {
RouterIntoService {
router: self,
_marker: PhantomData,
}
}
}
impl<B> Router<(), B>
where
B: HttpBody + Send + 'static,
{
impl Router {
/// Convert this router into a [`MakeService`], that is a [`Service`] whose
/// response is another service.
///
@ -368,13 +434,14 @@ where
}
}
impl<B> Service<Request<B>> for Router<(), B>
impl<B> Service<Request<B>> for Router<()>
where
B: HttpBody + Send + 'static,
B: HttpBody<Data = bytes::Bytes> + Send + 'static,
B::Error: Into<axum_core::BoxError>,
{
type Response = Response;
type Error = Infallible;
type Future = RouteFuture<B, Infallible>;
type Future = RouteFuture<Infallible>;
#[inline]
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
@ -383,17 +450,96 @@ where
#[inline]
fn call(&mut self, req: Request<B>) -> Self::Future {
let req = req.map(Body::new);
self.call_with_state(req, ())
}
}
enum Fallback<S, B, E = Infallible> {
Default(Route<B, E>),
Service(Route<B, E>),
BoxedHandler(BoxedIntoRoute<S, B, E>),
/// A [`Router`] converted into a borrowed [`Service`] with a fixed body type.
///
/// See [`Router::as_service`] for more details.
pub struct RouterAsService<'a, B, S = ()> {
router: &'a mut Router<S>,
_marker: PhantomData<B>,
}
impl<S, B, E> Fallback<S, B, E>
impl<'a, B> Service<Request<B>> for RouterAsService<'a, B, ()>
where
B: HttpBody<Data = bytes::Bytes> + Send + 'static,
B::Error: Into<axum_core::BoxError>,
{
type Response = Response;
type Error = Infallible;
type Future = RouteFuture<Infallible>;
#[inline]
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
<Router as Service<Request<B>>>::poll_ready(self.router, cx)
}
#[inline]
fn call(&mut self, req: Request<B>) -> Self::Future {
self.router.call(req)
}
}
impl<'a, B, S> fmt::Debug for RouterAsService<'a, B, S>
where
S: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RouterAsService")
.field("router", &self.router)
.finish()
}
}
/// A [`Router`] converted into an owned [`Service`] with a fixed body type.
///
/// See [`Router::into_service`] for more details.
pub struct RouterIntoService<B, S = ()> {
router: Router<S>,
_marker: PhantomData<B>,
}
impl<B> Service<Request<B>> for RouterIntoService<B, ()>
where
B: HttpBody<Data = bytes::Bytes> + Send + 'static,
B::Error: Into<axum_core::BoxError>,
{
type Response = Response;
type Error = Infallible;
type Future = RouteFuture<Infallible>;
#[inline]
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
<Router as Service<Request<B>>>::poll_ready(&mut self.router, cx)
}
#[inline]
fn call(&mut self, req: Request<B>) -> Self::Future {
self.router.call(req)
}
}
impl<B, S> fmt::Debug for RouterIntoService<B, S>
where
S: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RouterIntoService")
.field("router", &self.router)
.finish()
}
}
enum Fallback<S, E = Infallible> {
Default(Route<E>),
Service(Route<E>),
BoxedHandler(BoxedIntoRoute<S, E>),
}
impl<S, E> Fallback<S, E>
where
S: Clone,
{
@ -405,13 +551,11 @@ where
}
}
fn map<F, B2, E2>(self, f: F) -> Fallback<S, B2, E2>
fn map<F, E2>(self, f: F) -> Fallback<S, E2>
where
S: 'static,
B: 'static,
E: 'static,
F: FnOnce(Route<B, E>) -> Route<B2, E2> + Clone + Send + 'static,
B2: HttpBody + 'static,
F: FnOnce(Route<E>) -> Route<E2> + Clone + Send + 'static,
E2: 'static,
{
match self {
@ -421,7 +565,7 @@ where
}
}
fn with_state<S2>(self, state: S) -> Fallback<S2, B, E> {
fn with_state<S2>(self, state: S) -> Fallback<S2, E> {
match self {
Fallback::Default(route) => Fallback::Default(route),
Fallback::Service(route) => Fallback::Service(route),
@ -430,7 +574,7 @@ where
}
}
impl<S, B, E> Clone for Fallback<S, B, E> {
impl<S, E> Clone for Fallback<S, E> {
fn clone(&self) -> Self {
match self {
Self::Default(inner) => Self::Default(inner.clone()),
@ -440,7 +584,7 @@ impl<S, B, E> Clone for Fallback<S, B, E> {
}
}
impl<S, B, E> fmt::Debug for Fallback<S, B, E> {
impl<S, E> fmt::Debug for Fallback<S, E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Default(inner) => f.debug_tuple("Default").field(inner).finish(),
@ -451,24 +595,22 @@ impl<S, B, E> fmt::Debug for Fallback<S, B, E> {
}
#[allow(clippy::large_enum_variant)]
enum Endpoint<S, B> {
MethodRouter(MethodRouter<S, B>),
Route(Route<B>),
enum Endpoint<S> {
MethodRouter(MethodRouter<S>),
Route(Route),
}
impl<S, B> Endpoint<S, B>
impl<S> Endpoint<S>
where
B: HttpBody + Send + 'static,
S: Clone + Send + Sync + 'static,
{
fn layer<L, NewReqBody>(self, layer: L) -> Endpoint<S, NewReqBody>
fn layer<L>(self, layer: L) -> Endpoint<S>
where
L: Layer<Route<B>> + Clone + Send + 'static,
L::Service: Service<Request<NewReqBody>> + Clone + Send + 'static,
<L::Service as Service<Request<NewReqBody>>>::Response: IntoResponse + 'static,
<L::Service as Service<Request<NewReqBody>>>::Error: Into<Infallible> + 'static,
<L::Service as Service<Request<NewReqBody>>>::Future: Send + 'static,
NewReqBody: HttpBody + 'static,
L: Layer<Route> + Clone + Send + 'static,
L::Service: Service<Request<Body>> + Clone + Send + 'static,
<L::Service as Service<Request<Body>>>::Response: IntoResponse + 'static,
<L::Service as Service<Request<Body>>>::Error: Into<Infallible> + 'static,
<L::Service as Service<Request<Body>>>::Future: Send + 'static,
{
match self {
Endpoint::MethodRouter(method_router) => {
@ -479,7 +621,7 @@ where
}
}
impl<S, B> Clone for Endpoint<S, B> {
impl<S> Clone for Endpoint<S> {
fn clone(&self) -> Self {
match self {
Self::MethodRouter(inner) => Self::MethodRouter(inner.clone()),
@ -488,7 +630,7 @@ impl<S, B> Clone for Endpoint<S, B> {
}
}
impl<S, B> fmt::Debug for Endpoint<S, B> {
impl<S> fmt::Debug for Endpoint<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::MethodRouter(method_router) => {
@ -499,11 +641,11 @@ impl<S, B> fmt::Debug for Endpoint<S, B> {
}
}
struct SuperFallback<S, B>(SyncWrapper<PathRouter<S, B, true>>);
struct SuperFallback<S>(SyncWrapper<PathRouter<S, true>>);
#[test]
#[allow(warnings)]
fn traits() {
use crate::test_helpers::*;
assert_send::<Router<(), ()>>();
assert_send::<Router<()>>();
}

View file

@ -1,4 +1,4 @@
use crate::body::HttpBody;
use crate::body::Body;
use axum_core::response::IntoResponse;
use http::Request;
use matchit::MatchError;
@ -11,21 +11,20 @@ use super::{
RouteId, NEST_TAIL_PARAM,
};
pub(super) struct PathRouter<S, B, const IS_FALLBACK: bool> {
routes: HashMap<RouteId, Endpoint<S, B>>,
pub(super) struct PathRouter<S, const IS_FALLBACK: bool> {
routes: HashMap<RouteId, Endpoint<S>>,
node: Arc<Node>,
prev_route_id: RouteId,
}
impl<S, B, const IS_FALLBACK: bool> PathRouter<S, B, IS_FALLBACK>
impl<S, const IS_FALLBACK: bool> PathRouter<S, IS_FALLBACK>
where
B: HttpBody + Send + 'static,
S: Clone + Send + Sync + 'static,
{
pub(super) fn route(
&mut self,
path: &str,
method_router: MethodRouter<S, B>,
method_router: MethodRouter<S>,
) -> Result<(), Cow<'static, str>> {
fn validate_path(path: &str) -> Result<(), &'static str> {
if path.is_empty() {
@ -72,7 +71,7 @@ where
service: T,
) -> Result<(), Cow<'static, str>>
where
T: Service<Request<B>, Error = Infallible> + Clone + Send + 'static,
T: Service<Request<Body>, Error = Infallible> + Clone + Send + 'static,
T::Response: IntoResponse,
T::Future: Send + 'static,
{
@ -82,7 +81,7 @@ where
pub(super) fn route_endpoint(
&mut self,
path: &str,
endpoint: Endpoint<S, B>,
endpoint: Endpoint<S>,
) -> Result<(), Cow<'static, str>> {
if path.is_empty() {
return Err("Paths must start with a `/`. Use \"/\" for root routes".into());
@ -109,7 +108,7 @@ where
pub(super) fn merge(
&mut self,
other: PathRouter<S, B, IS_FALLBACK>,
other: PathRouter<S, IS_FALLBACK>,
) -> Result<(), Cow<'static, str>> {
let PathRouter {
routes,
@ -134,7 +133,7 @@ where
pub(super) fn nest(
&mut self,
path: &str,
router: PathRouter<S, B, IS_FALLBACK>,
router: PathRouter<S, IS_FALLBACK>,
) -> Result<(), Cow<'static, str>> {
let prefix = validate_nest_path(path);
@ -167,7 +166,7 @@ where
pub(super) fn nest_service<T>(&mut self, path: &str, svc: T) -> Result<(), Cow<'static, str>>
where
T: Service<Request<B>, Error = Infallible> + Clone + Send + 'static,
T: Service<Request<Body>, Error = Infallible> + Clone + Send + 'static,
T::Response: IntoResponse,
T::Future: Send + 'static,
{
@ -196,14 +195,13 @@ where
Ok(())
}
pub(super) fn layer<L, NewReqBody>(self, layer: L) -> PathRouter<S, NewReqBody, IS_FALLBACK>
pub(super) fn layer<L>(self, layer: L) -> PathRouter<S, IS_FALLBACK>
where
L: Layer<Route<B>> + Clone + Send + 'static,
L::Service: Service<Request<NewReqBody>> + Clone + Send + 'static,
<L::Service as Service<Request<NewReqBody>>>::Response: IntoResponse + 'static,
<L::Service as Service<Request<NewReqBody>>>::Error: Into<Infallible> + 'static,
<L::Service as Service<Request<NewReqBody>>>::Future: Send + 'static,
NewReqBody: HttpBody + 'static,
L: Layer<Route> + Clone + Send + 'static,
L::Service: Service<Request<Body>> + Clone + Send + 'static,
<L::Service as Service<Request<Body>>>::Response: IntoResponse + 'static,
<L::Service as Service<Request<Body>>>::Error: Into<Infallible> + 'static,
<L::Service as Service<Request<Body>>>::Future: Send + 'static,
{
let routes = self
.routes
@ -224,11 +222,11 @@ where
#[track_caller]
pub(super) fn route_layer<L>(self, layer: L) -> Self
where
L: Layer<Route<B>> + Clone + Send + 'static,
L::Service: Service<Request<B>> + Clone + Send + 'static,
<L::Service as Service<Request<B>>>::Response: IntoResponse + 'static,
<L::Service as Service<Request<B>>>::Error: Into<Infallible> + 'static,
<L::Service as Service<Request<B>>>::Future: Send + 'static,
L: Layer<Route> + Clone + Send + 'static,
L::Service: Service<Request<Body>> + Clone + Send + 'static,
<L::Service as Service<Request<Body>>>::Response: IntoResponse + 'static,
<L::Service as Service<Request<Body>>>::Error: Into<Infallible> + 'static,
<L::Service as Service<Request<Body>>>::Future: Send + 'static,
{
if self.routes.is_empty() {
panic!(
@ -253,12 +251,12 @@ where
}
}
pub(super) fn with_state<S2>(self, state: S) -> PathRouter<S2, B, IS_FALLBACK> {
pub(super) fn with_state<S2>(self, state: S) -> PathRouter<S2, IS_FALLBACK> {
let routes = self
.routes
.into_iter()
.map(|(id, endpoint)| {
let endpoint: Endpoint<S2, B> = match endpoint {
let endpoint: Endpoint<S2> = match endpoint {
Endpoint::MethodRouter(method_router) => {
Endpoint::MethodRouter(method_router.with_state(state.clone()))
}
@ -277,9 +275,9 @@ where
pub(super) fn call_with_state(
&mut self,
mut req: Request<B>,
mut req: Request<Body>,
state: S,
) -> Result<RouteFuture<B, Infallible>, (Request<B>, S)> {
) -> Result<RouteFuture<Infallible>, (Request<Body>, S)> {
#[cfg(feature = "original-uri")]
{
use crate::extract::OriginalUri;
@ -329,7 +327,7 @@ where
}
}
pub(super) fn replace_endpoint(&mut self, path: &str, endpoint: Endpoint<S, B>) {
pub(super) fn replace_endpoint(&mut self, path: &str, endpoint: Endpoint<S>) {
match self.node.at(path) {
Ok(match_) => {
let id = *match_.value;
@ -352,7 +350,7 @@ where
}
}
impl<B, S, const IS_FALLBACK: bool> Default for PathRouter<S, B, IS_FALLBACK> {
impl<S, const IS_FALLBACK: bool> Default for PathRouter<S, IS_FALLBACK> {
fn default() -> Self {
Self {
routes: Default::default(),
@ -362,7 +360,7 @@ impl<B, S, const IS_FALLBACK: bool> Default for PathRouter<S, B, IS_FALLBACK> {
}
}
impl<S, B, const IS_FALLBACK: bool> fmt::Debug for PathRouter<S, B, IS_FALLBACK> {
impl<S, const IS_FALLBACK: bool> fmt::Debug for PathRouter<S, IS_FALLBACK> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PathRouter")
.field("routes", &self.routes)
@ -371,7 +369,7 @@ impl<S, B, const IS_FALLBACK: bool> fmt::Debug for PathRouter<S, B, IS_FALLBACK>
}
}
impl<S, B, const IS_FALLBACK: bool> Clone for PathRouter<S, B, IS_FALLBACK> {
impl<S, const IS_FALLBACK: bool> Clone for PathRouter<S, IS_FALLBACK> {
fn clone(&self) -> Self {
Self {
routes: self.routes.clone(),

View file

@ -27,12 +27,12 @@ use tower_service::Service;
///
/// You normally shouldn't need to care about this type. It's used in
/// [`Router::layer`](super::Router::layer).
pub struct Route<B = Body, E = Infallible>(BoxCloneService<Request<B>, Response, E>);
pub struct Route<E = Infallible>(BoxCloneService<Request<Body>, Response, E>);
impl<B, E> Route<B, E> {
impl<E> Route<E> {
pub(crate) fn new<T>(svc: T) -> Self
where
T: Service<Request<B>, Error = E> + Clone + Send + 'static,
T: Service<Request<Body>, Error = E> + Clone + Send + 'static,
T::Response: IntoResponse + 'static,
T::Future: Send + 'static,
{
@ -43,22 +43,22 @@ impl<B, E> Route<B, E> {
pub(crate) fn oneshot_inner(
&mut self,
req: Request<B>,
) -> Oneshot<BoxCloneService<Request<B>, Response, E>, Request<B>> {
req: Request<Body>,
) -> Oneshot<BoxCloneService<Request<Body>, Response, E>, Request<Body>> {
self.0.clone().oneshot(req)
}
pub(crate) fn layer<L, NewReqBody, NewError>(self, layer: L) -> Route<NewReqBody, NewError>
pub(crate) fn layer<L, NewError>(self, layer: L) -> Route<NewError>
where
L: Layer<Route<B, E>> + Clone + Send + 'static,
L::Service: Service<Request<NewReqBody>> + Clone + Send + 'static,
<L::Service as Service<Request<NewReqBody>>>::Response: IntoResponse + 'static,
<L::Service as Service<Request<NewReqBody>>>::Error: Into<NewError> + 'static,
<L::Service as Service<Request<NewReqBody>>>::Future: Send + 'static,
NewReqBody: 'static,
L: Layer<Route<E>> + Clone + Send + 'static,
L::Service: Service<Request<Body>> + Clone + Send + 'static,
<L::Service as Service<Request<Body>>>::Response: IntoResponse + 'static,
<L::Service as Service<Request<Body>>>::Error: Into<NewError> + 'static,
<L::Service as Service<Request<Body>>>::Future: Send + 'static,
NewError: 'static,
{
let layer = ServiceBuilder::new()
.map_request(|req: Request<_>| req.map(Body::new))
.map_err(Into::into)
.layer(MapResponseLayer::new(IntoResponse::into_response))
.layer(layer)
@ -68,25 +68,26 @@ impl<B, E> Route<B, E> {
}
}
impl<B, E> Clone for Route<B, E> {
impl<E> Clone for Route<E> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<B, E> fmt::Debug for Route<B, E> {
impl<E> fmt::Debug for Route<E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Route").finish()
}
}
impl<B, E> Service<Request<B>> for Route<B, E>
impl<B, E> Service<Request<B>> for Route<E>
where
B: HttpBody,
B: HttpBody<Data = bytes::Bytes> + Send + 'static,
B::Error: Into<axum_core::BoxError>,
{
type Response = Response;
type Error = E;
type Future = RouteFuture<B, E>;
type Future = RouteFuture<E>;
#[inline]
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
@ -95,15 +96,16 @@ where
#[inline]
fn call(&mut self, req: Request<B>) -> Self::Future {
let req = req.map(Body::new);
RouteFuture::from_future(self.oneshot_inner(req))
}
}
pin_project! {
/// Response future for [`Route`].
pub struct RouteFuture<B, E> {
pub struct RouteFuture<E> {
#[pin]
kind: RouteFutureKind<B, E>,
kind: RouteFutureKind<E>,
strip_body: bool,
allow_header: Option<Bytes>,
}
@ -111,12 +113,12 @@ pin_project! {
pin_project! {
#[project = RouteFutureKindProj]
enum RouteFutureKind<B, E> {
enum RouteFutureKind<E> {
Future {
#[pin]
future: Oneshot<
BoxCloneService<Request<B>, Response, E>,
Request<B>,
BoxCloneService<Request<Body>, Response, E>,
Request<Body>,
>,
},
Response {
@ -125,9 +127,9 @@ pin_project! {
}
}
impl<B, E> RouteFuture<B, E> {
impl<E> RouteFuture<E> {
pub(crate) fn from_future(
future: Oneshot<BoxCloneService<Request<B>, Response, E>, Request<B>>,
future: Oneshot<BoxCloneService<Request<Body>, Response, E>, Request<Body>>,
) -> Self {
Self {
kind: RouteFutureKind::Future { future },
@ -147,10 +149,7 @@ impl<B, E> RouteFuture<B, E> {
}
}
impl<B, E> Future for RouteFuture<B, E>
where
B: HttpBody,
{
impl<E> Future for RouteFuture<E> {
type Output = Result<Response, E>;
#[inline]
@ -217,22 +216,19 @@ fn set_content_length(size_hint: http_body::SizeHint, headers: &mut HeaderMap) {
pin_project! {
/// A [`RouteFuture`] that always yields a [`Response`].
pub struct InfallibleRouteFuture<B> {
pub struct InfallibleRouteFuture {
#[pin]
future: RouteFuture<B, Infallible>,
future: RouteFuture<Infallible>,
}
}
impl<B> InfallibleRouteFuture<B> {
pub(crate) fn new(future: RouteFuture<B, Infallible>) -> Self {
impl InfallibleRouteFuture {
pub(crate) fn new(future: RouteFuture<Infallible>) -> Self {
Self { future }
}
}
impl<B> Future for InfallibleRouteFuture<B>
where
B: HttpBody,
{
impl Future for InfallibleRouteFuture {
type Output = Response;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {

View file

@ -176,7 +176,7 @@ async fn fallback_inherited_into_nested_router_service() {
.with_state("inner");
// with a different state
let app = Router::<()>::new()
let app = Router::new()
.nest_service("/foo", inner)
.fallback(outer_fallback);
@ -198,7 +198,7 @@ async fn fallback_inherited_into_nested_opaque_service() {
.boxed_clone();
// with a different state
let app = Router::<()>::new()
let app = Router::new()
.nest_service("/foo", inner)
.fallback(outer_fallback);

View file

@ -1,5 +1,5 @@
use crate::{
body::{Bytes, Empty},
body::{Body, Bytes, Empty},
error_handling::HandleErrorLayer,
extract::{self, DefaultBodyLimit, FromRef, Path, State},
handler::{Handler, HandlerWithoutStateExt},
@ -16,7 +16,6 @@ use crate::{
};
use futures_util::stream::StreamExt;
use http::{header::ALLOW, header::CONTENT_LENGTH, HeaderMap, Request, Response, StatusCode, Uri};
use hyper::Body;
use serde::Deserialize;
use serde_json::json;
use std::{
@ -206,7 +205,7 @@ async fn middleware_on_single_route() {
#[crate::test]
async fn service_in_bottom() {
async fn handler(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
Ok(Response::new(hyper::Body::empty()))
Ok(Response::new(Body::empty()))
}
let app = Router::new().route("/", get_service(service_fn(handler)));
@ -659,7 +658,7 @@ async fn body_limited_by_default() {
println!("calling {uri}");
let stream = futures_util::stream::repeat("a".repeat(1000)).map(Ok::<_, hyper::Error>);
let body = Body::wrap_stream(stream);
let body = reqwest::Body::wrap_stream(stream);
let res_future = client
.post(uri)
@ -683,7 +682,7 @@ async fn disabling_the_default_limit() {
let client = TestClient::new(app);
// `DEFAULT_LIMIT` is 2mb so make a body larger than that
let body = Body::from("a".repeat(3_000_000));
let body = reqwest::Body::from("a".repeat(3_000_000));
let res = client.post("/").body(body).send().await;
@ -724,14 +723,14 @@ async fn changing_the_default_limit() {
let res = client
.post("/")
.body(Body::from("a".repeat(new_limit)))
.body(reqwest::Body::from("a".repeat(new_limit)))
.send()
.await;
assert_eq!(res.status(), StatusCode::OK);
let res = client
.post("/")
.body(Body::from("a".repeat(new_limit + 1)))
.body(reqwest::Body::from("a".repeat(new_limit + 1)))
.send()
.await;
assert_eq!(res.status(), StatusCode::PAYLOAD_TOO_LARGE);
@ -755,7 +754,7 @@ async fn limited_body_with_streaming_body() {
let stream = futures_util::stream::iter(vec![Ok::<_, hyper::Error>("a".repeat(LIMIT))]);
let res = client
.post("/")
.body(Body::wrap_stream(stream))
.body(reqwest::Body::wrap_stream(stream))
.send()
.await;
assert_eq!(res.status(), StatusCode::OK);
@ -763,7 +762,7 @@ async fn limited_body_with_streaming_body() {
let stream = futures_util::stream::iter(vec![Ok::<_, hyper::Error>("a".repeat(LIMIT * 2))]);
let res = client
.post("/")
.body(Body::wrap_stream(stream))
.body(reqwest::Body::wrap_stream(stream))
.send()
.await;
assert_eq!(res.status(), StatusCode::PAYLOAD_TOO_LARGE);

View file

@ -264,7 +264,7 @@ async fn multiple_top_level_nests() {
#[crate::test]
#[should_panic(expected = "Invalid route: nested routes cannot contain wildcards (*)")]
async fn nest_cannot_contain_wildcards() {
_ = Router::<(), Body>::new().nest("/one/*rest", Router::new());
_ = Router::<()>::new().nest("/one/*rest", Router::new());
}
#[crate::test]

View file

@ -6,7 +6,7 @@
use axum::{
async_trait,
body::{self, BoxBody, Bytes, Full},
body::{Body, Bytes},
extract::FromRequest,
http::{Request, StatusCode},
middleware::{self, Next},
@ -16,7 +16,6 @@ use axum::{
};
use std::net::SocketAddr;
use tower::ServiceBuilder;
use tower_http::ServiceBuilderExt;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
#[tokio::main]
@ -29,11 +28,9 @@ async fn main() {
.with(tracing_subscriber::fmt::layer())
.init();
let app = Router::new().route("/", post(handler)).layer(
ServiceBuilder::new()
.map_request_body(body::boxed)
.layer(middleware::from_fn(print_request_body)),
);
let app = Router::new()
.route("/", post(handler))
.layer(ServiceBuilder::new().layer(middleware::from_fn(print_request_body)));
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
tracing::debug!("listening on {}", addr);
@ -45,8 +42,8 @@ async fn main() {
// middleware that shows how to consume the request body upfront
async fn print_request_body(
request: Request<BoxBody>,
next: Next<BoxBody>,
request: Request<Body>,
next: Next<Body>,
) -> Result<impl IntoResponse, Response> {
let request = buffer_request_body(request).await?;
@ -55,7 +52,7 @@ async fn print_request_body(
// the trick is to take the request apart, buffer the body, do what you need to do, then put
// the request back together
async fn buffer_request_body(request: Request<BoxBody>) -> Result<Request<BoxBody>, Response> {
async fn buffer_request_body(request: Request<Body>) -> Result<Request<Body>, Response> {
let (parts, body) = request.into_parts();
// this wont work if the body is an long running stream
@ -65,7 +62,7 @@ async fn buffer_request_body(request: Request<BoxBody>) -> Result<Request<BoxBod
do_thing_with_request_body(bytes.clone());
Ok(Request::from_parts(parts, body::boxed(Full::from(bytes))))
Ok(Request::from_parts(parts, Body::from(bytes)))
}
fn do_thing_with_request_body(bytes: Bytes) {
@ -81,13 +78,13 @@ struct BufferRequestBody(Bytes);
// we must implement `FromRequest` (and not `FromRequestParts`) to consume the body
#[async_trait]
impl<S> FromRequest<S, BoxBody> for BufferRequestBody
impl<S> FromRequest<S> for BufferRequestBody
where
S: Send + Sync,
{
type Rejection = Response;
async fn from_request(req: Request<BoxBody>, state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(req: Request<Body>, state: &S) -> Result<Self, Self::Rejection> {
let body = Bytes::from_request(req, state)
.await
.map_err(|err| err.into_response())?;

View file

@ -6,6 +6,7 @@
//! - Complexity: Manually implementing `FromRequest` results on more complex code
use axum::{
async_trait,
body::Body,
extract::{rejection::JsonRejection, FromRequest, MatchedPath},
http::Request,
http::StatusCode,
@ -22,15 +23,14 @@ pub async fn handler(Json(value): Json<Value>) -> impl IntoResponse {
pub struct Json<T>(pub T);
#[async_trait]
impl<S, B, T> FromRequest<S, B> for Json<T>
impl<S, T> FromRequest<S> for Json<T>
where
axum::Json<T>: FromRequest<S, B, Rejection = JsonRejection>,
axum::Json<T>: FromRequest<S, Rejection = JsonRejection>,
S: Send + Sync,
B: Send + 'static,
{
type Rejection = (StatusCode, axum::Json<Value>);
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(req: Request<Body>, state: &S) -> Result<Self, Self::Rejection> {
let (mut parts, body) = req.into_parts();
// We can use other extractors to provide better rejection messages.

View file

@ -37,8 +37,9 @@ async fn main() {
let router_svc = Router::new().route("/", get(|| async { "Hello, World!" }));
let service = tower::service_fn(move |req: Request<Body>| {
let service = tower::service_fn(move |req: Request<_>| {
let router_svc = router_svc.clone();
let req = req.map(Body::new);
async move {
if req.method() == Method::CONNECT {
proxy(req).await

View file

@ -8,13 +8,11 @@ use axum::{routing::get, Router};
use std::net::SocketAddr;
use tokio::net::TcpListener;
use tower_http::trace::TraceLayer;
use tower_hyper_http_body_compat::{
HttpBody1ToHttpBody04, TowerService03HttpServiceAsHyper1HttpService,
};
use tower_hyper_http_body_compat::TowerService03HttpServiceAsHyper1HttpService;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
// this is hyper 1.0
use hyper::{body::Incoming, server::conn::http1};
use hyper::server::conn::http1;
#[tokio::main]
async fn main() {
@ -26,8 +24,7 @@ async fn main() {
.with(tracing_subscriber::fmt::layer())
.init();
// you have to use `HttpBody1ToHttpBody04<Incoming>` as the second type parameter to `Router`
let app: Router<_, HttpBody1ToHttpBody04<Incoming>> = Router::new()
let app = Router::new()
.route("/", get(|| async { "Hello, World!" }))
// we can still add regular tower middleware
.layer(TraceLayer::new_for_http());

View file

@ -1,7 +1,7 @@
use openssl::ssl::{Ssl, SslAcceptor, SslFiletype, SslMethod};
use tokio_openssl::SslStream;
use axum::{extract::ConnectInfo, routing::get, Router};
use axum::{body::Body, extract::ConnectInfo, http::Request, routing::get, Router};
use futures_util::future::poll_fn;
use hyper::server::{
accept::Accept,
@ -68,7 +68,7 @@ async fn main() {
let protocol = protocol.clone();
let svc = app.make_service(&stream);
let svc = MakeService::<_, Request<Body>>::make_service(&mut app, &stream);
tokio::spawn(async move {
let ssl = Ssl::new(acceptor.context()).unwrap();

View file

@ -4,7 +4,7 @@
//! cargo run -p example-low-level-rustls
//! ```
use axum::{extract::ConnectInfo, routing::get, Router};
use axum::{body::Body, extract::ConnectInfo, http::Request, routing::get, Router};
use futures_util::future::poll_fn;
use hyper::server::{
accept::Accept,
@ -24,7 +24,7 @@ use tokio_rustls::{
rustls::{Certificate, PrivateKey, ServerConfig},
TlsAcceptor,
};
use tower::MakeService;
use tower::make::MakeService;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
#[tokio::main]
@ -53,7 +53,7 @@ async fn main() {
let protocol = Arc::new(Http::new());
let mut app = Router::new()
let mut app = Router::<()>::new()
.route("/", get(handler))
.into_make_service_with_connect_info::<SocketAddr>();
@ -67,7 +67,7 @@ async fn main() {
let protocol = protocol.clone();
let svc = app.make_service(&stream);
let svc = MakeService::<_, Request<Body>>::make_service(&mut app, &stream);
tokio::spawn(async move {
if let Ok(stream) = acceptor.accept(stream).await {

View file

@ -8,6 +8,7 @@
use axum::{
async_trait,
body::Body,
extract::FromRequest,
http::{header::CONTENT_TYPE, Request, StatusCode},
response::{IntoResponse, Response},
@ -51,17 +52,16 @@ async fn handler(JsonOrForm(payload): JsonOrForm<Payload>) {
struct JsonOrForm<T>(T);
#[async_trait]
impl<S, B, T> FromRequest<S, B> for JsonOrForm<T>
impl<S, T> FromRequest<S> for JsonOrForm<T>
where
B: Send + 'static,
S: Send + Sync,
Json<T>: FromRequest<(), B>,
Form<T>: FromRequest<(), B>,
Json<T>: FromRequest<()>,
Form<T>: FromRequest<()>,
T: 'static,
{
type Rejection = Response;
async fn from_request(req: Request<B>, _state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(req: Request<Body>, _state: &S) -> Result<Self, Self::Rejection> {
let content_type_header = req.headers().get(CONTENT_TYPE);
let content_type = content_type_header.and_then(|value| value.to_str().ok());

View file

@ -8,12 +8,14 @@
//! ```
use axum::{
body::Body,
extract::State,
http::{uri::Uri, Request, Response},
http::{uri::Uri, Request},
response::{IntoResponse, Response},
routing::get,
Router,
};
use hyper::{client::HttpConnector, Body};
use hyper::client::HttpConnector;
use std::net::SocketAddr;
type Client = hyper::client::Client<HttpConnector, Body>;
@ -22,7 +24,7 @@ type Client = hyper::client::Client<HttpConnector, Body>;
async fn main() {
tokio::spawn(server());
let client = Client::new();
let client: Client = hyper::Client::builder().build(HttpConnector::new());
let app = Router::new().route("/", get(handler)).with_state(client);
@ -34,7 +36,7 @@ async fn main() {
.unwrap();
}
async fn handler(State(client): State<Client>, mut req: Request<Body>) -> Response<Body> {
async fn handler(State(client): State<Client>, mut req: Request<Body>) -> Response {
let path = req.uri().path();
let path_query = req
.uri()
@ -46,7 +48,7 @@ async fn handler(State(client): State<Client>, mut req: Request<Body>) -> Respon
*req.uri_mut() = Uri::try_from(uri).unwrap();
client.request(req).await.unwrap()
client.request(req).await.unwrap().into_response()
}
async fn server() {

View file

@ -71,7 +71,10 @@ fn using_serve_dir_with_handler_as_service() -> Router {
(StatusCode::NOT_FOUND, "Not found")
}
let serve_dir = ServeDir::new("assets").not_found_service(handle_404.into_service());
// you can convert handler function to service
let service = handle_404.into_service();
let serve_dir = ServeDir::new("assets").not_found_service(service);
Router::new()
.route("/foo", get(|| async { "Hi from /foo" }))

View file

@ -5,9 +5,9 @@
//! ```
use axum::{
body::Bytes,
extract::{BodyStream, Multipart, Path},
http::StatusCode,
body::{Body, Bytes},
extract::{Multipart, Path},
http::{Request, StatusCode},
response::{Html, Redirect},
routing::{get, post},
BoxError, Router,
@ -52,9 +52,9 @@ async fn main() {
// POST'ing to `/file/foo.txt` will create a file called `foo.txt`.
async fn save_request_body(
Path(file_name): Path<String>,
body: BodyStream,
request: Request<Body>,
) -> Result<(), (StatusCode, String)> {
stream_to_file(&file_name, body).await
stream_to_file(&file_name, request.into_body()).await
}
// Handler that returns HTML for a multipart form.

View file

@ -148,7 +148,7 @@ mod tests {
.request(
Request::builder()
.uri(format!("http://{}", addr))
.body(Body::empty())
.body(hyper::Body::empty())
.unwrap(),
)
.await
@ -165,11 +165,21 @@ mod tests {
let mut app = app();
let request = Request::builder().uri("/").body(Body::empty()).unwrap();
let response = app.ready().await.unwrap().call(request).await.unwrap();
let response = ServiceExt::<Request<Body>>::ready(&mut app)
.await
.unwrap()
.call(request)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
let request = Request::builder().uri("/").body(Body::empty()).unwrap();
let response = app.ready().await.unwrap().call(request).await.unwrap();
let response = ServiceExt::<Request<Body>>::ready(&mut app)
.await
.unwrap()
.call(request)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
}
@ -186,7 +196,14 @@ mod tests {
.uri("/requires-connect-into")
.body(Body::empty())
.unwrap();
let response = app.ready().await.unwrap().call(request).await.unwrap();
let response = app
.as_service()
.ready()
.await
.unwrap()
.call(request)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
}
}

View file

@ -12,6 +12,7 @@
use async_trait::async_trait;
use axum::{
body::Body,
extract::{rejection::FormRejection, Form, FromRequest},
http::{Request, StatusCode},
response::{Html, IntoResponse, Response},
@ -61,16 +62,15 @@ async fn handler(ValidatedForm(input): ValidatedForm<NameInput>) -> Html<String>
pub struct ValidatedForm<T>(pub T);
#[async_trait]
impl<T, S, B> FromRequest<S, B> for ValidatedForm<T>
impl<T, S> FromRequest<S> for ValidatedForm<T>
where
T: DeserializeOwned + Validate,
S: Send + Sync,
Form<T>: FromRequest<S, B, Rejection = FormRejection>,
B: Send + 'static,
Form<T>: FromRequest<S, Rejection = FormRejection>,
{
type Rejection = ServerError;
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
async fn from_request(req: Request<Body>, state: &S) -> Result<Self, Self::Rejection> {
let Form(value) = Form::<T>::from_request(req, state).await?;
value.validate()?;
Ok(ValidatedForm(value))