mirror of
https://github.com/tokio-rs/axum.git
synced 2026-08-26 00:00:23 +02:00
Add type safe state extractor (#1155)
* begin threading the state through * Pass state to extractors * make state extractor work * make sure nesting with different states work * impl Service for MethodRouter<()> * Fix some of axum-macro's tests * Implement more traits for `State` * Update examples to use `State` * consistent naming of request body param * swap type params * Default the state param to () * fix docs references * Docs and handler state refactoring * docs clean ups * more consistent naming * when does MethodRouter implement Service? * add missing docs * use `Router`'s default state type param * changelog * don't use default type param for FromRequest and RequestParts probably safer for library authors so you don't accidentally forget * fix examples * minor docs tweaks * clarify how to convert handlers into services * group methods in one impl block * make sure merged `MethodRouter`s can access state * fix docs link * test merge with same state type * Document how to access state from middleware * Port cookie extractors to use state to extract keys (#1250) * Updates ECOSYSTEM with a new sample project (#1252) * Avoid unhelpful compiler suggestion (#1251) * fix docs typo * document how library authors should access state * Add `RequestParts::with_state` * fix example * apply suggestions from review * add relevant changes to axum-extra and axum-core changelogs * Add `route_service_with_tsr` * fix trybuild expectations * make sure `SpaRouter` works with routers that have state * Change order of type params on FromRequest and RequestParts * reverse order of `RequestParts::with_state` args to match type params * Add `FromRef` trait (#1268) * Add `FromRef` trait * Remove unnecessary type params * format * fix docs link * format examples * Avoid unnecessary `MethodRouter` * apply suggestions from review Co-authored-by: Dani Pardo <[email protected]> Co-authored-by: Jonas Platte <[email protected]>
This commit is contained in:
co-authored by
Dani Pardo
Jonas Platte
parent
90dbd52ee4
commit
423308de3c
@@ -69,7 +69,7 @@ let some_fallible_service = tower::service_fn(|_req| async {
|
||||
Ok::<_, anyhow::Error>(Response::new(Body::empty()))
|
||||
});
|
||||
|
||||
let app = Router::new().route(
|
||||
let app = Router::new().route_service(
|
||||
"/",
|
||||
// we cannot route to `some_fallible_service` directly since it might fail.
|
||||
// we have to use `handle_error` which converts its errors into responses
|
||||
|
||||
@@ -421,13 +421,14 @@ use http::{StatusCode, header::{HeaderValue, USER_AGENT}};
|
||||
struct ExtractUserAgent(HeaderValue);
|
||||
|
||||
#[async_trait]
|
||||
impl<B> FromRequest<B> for ExtractUserAgent
|
||||
impl<S, B> FromRequest<S, B> for ExtractUserAgent
|
||||
where
|
||||
B: Send,
|
||||
S: Send,
|
||||
{
|
||||
type Rejection = (StatusCode, &'static str);
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
if let Some(user_agent) = req.headers().get(USER_AGENT) {
|
||||
Ok(ExtractUserAgent(user_agent.clone()))
|
||||
} else {
|
||||
@@ -472,13 +473,14 @@ struct AuthenticatedUser {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<B> FromRequest<B> for AuthenticatedUser
|
||||
impl<S, B> FromRequest<S, B> for AuthenticatedUser
|
||||
where
|
||||
B: Send,
|
||||
S: Send,
|
||||
{
|
||||
type Rejection = Response;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
let TypedHeader(Authorization(token)) =
|
||||
TypedHeader::<Authorization<Bearer>>::from_request(req)
|
||||
.await
|
||||
@@ -633,7 +635,7 @@ fn token_is_valid(token: &str) -> bool {
|
||||
}
|
||||
|
||||
let app = Router::new().layer(middleware::from_fn(auth_middleware));
|
||||
# let _: Router = app;
|
||||
# let _: Router<()> = app;
|
||||
```
|
||||
|
||||
[`body::Body`]: crate::body::Body
|
||||
|
||||
@@ -11,7 +11,7 @@ use axum::{
|
||||
http::{StatusCode, Method, Uri},
|
||||
};
|
||||
|
||||
let handler = get(|| async {}).fallback(fallback.into_service());
|
||||
let handler = get(|| async {}).fallback(fallback);
|
||||
|
||||
let app = Router::new().route("/", handler);
|
||||
|
||||
@@ -36,11 +36,9 @@ use axum::{
|
||||
http::{StatusCode, Uri},
|
||||
};
|
||||
|
||||
let one = get(|| async {})
|
||||
.fallback(fallback_one.into_service());
|
||||
let one = get(|| async {}).fallback(fallback_one);
|
||||
|
||||
let two = post(|| async {})
|
||||
.fallback(fallback_two.into_service());
|
||||
let two = post(|| async {}).fallback(fallback_two);
|
||||
|
||||
let method_route = one.merge(two);
|
||||
|
||||
|
||||
+120
-6
@@ -6,7 +6,8 @@
|
||||
- [Ordering](#ordering)
|
||||
- [Writing middleware](#writing-middleware)
|
||||
- [Routing to services/middleware and backpressure](#routing-to-servicesmiddleware-and-backpressure)
|
||||
- [Sharing state between handlers and middleware](#sharing-state-between-handlers-and-middleware)
|
||||
- [Accessing state in middleware](#accessing-state-in-middleware)
|
||||
- [Passing state from middleware to handlers](#passing-state-from-middleware-to-handlers)
|
||||
|
||||
# Intro
|
||||
|
||||
@@ -95,7 +96,7 @@ let app = Router::new()
|
||||
.layer(layer_one)
|
||||
.layer(layer_two)
|
||||
.layer(layer_three);
|
||||
# let app: Router<axum::body::Body> = app;
|
||||
# let _: Router<(), axum::body::Body> = app;
|
||||
```
|
||||
|
||||
Think of the middleware as being layered like an onion where each new layer
|
||||
@@ -154,7 +155,7 @@ let app = Router::new()
|
||||
.layer(layer_two)
|
||||
.layer(layer_three),
|
||||
);
|
||||
# let app: Router<axum::body::Body> = app;
|
||||
# let _: Router<(), axum::body::Body> = app;
|
||||
```
|
||||
|
||||
`ServiceBuilder` works by composing all layers into one such that they run top
|
||||
@@ -386,9 +387,119 @@ Also note that handlers created from async functions don't care about
|
||||
backpressure and are always ready. So if you're not using any Tower
|
||||
middleware you don't have to worry about any of this.
|
||||
|
||||
# Sharing state between handlers and middleware
|
||||
# Accessing state in middleware
|
||||
|
||||
State can be shared between middleware and handlers using [request extensions]:
|
||||
Handlers can access state using the [`State`] extractor but this isn't available
|
||||
to middleware. Instead you have to pass the state directly to middleware using
|
||||
either closure captures (for [`axum::middleware::from_fn`]) or regular struct
|
||||
fields (if you're implementing a [`tower::Layer`])
|
||||
|
||||
## Accessing state in `axum::middleware::from_fn`
|
||||
|
||||
```rust
|
||||
use axum::{
|
||||
Router,
|
||||
routing::get,
|
||||
middleware::{self, Next},
|
||||
response::Response,
|
||||
extract::State,
|
||||
http::Request,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct AppState {}
|
||||
|
||||
async fn my_middleware<B>(
|
||||
state: AppState,
|
||||
req: Request<B>,
|
||||
next: Next<B>,
|
||||
) -> Response {
|
||||
next.run(req).await
|
||||
}
|
||||
|
||||
async fn handler(_: State<AppState>) {}
|
||||
|
||||
let state = AppState {};
|
||||
|
||||
let app = Router::with_state(state.clone())
|
||||
.route("/", get(handler))
|
||||
.layer(middleware::from_fn(move |req, next| {
|
||||
my_middleware(state.clone(), req, next)
|
||||
}));
|
||||
# let _: Router<_> = app;
|
||||
```
|
||||
|
||||
## Accessing state in custom `tower::Layer`s
|
||||
|
||||
```rust
|
||||
use axum::{
|
||||
Router,
|
||||
routing::get,
|
||||
middleware::{self, Next},
|
||||
response::Response,
|
||||
extract::State,
|
||||
http::Request,
|
||||
};
|
||||
use tower::{Layer, Service};
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct AppState {}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct MyLayer {
|
||||
state: AppState,
|
||||
}
|
||||
|
||||
impl<S> Layer<S> for MyLayer {
|
||||
type Service = MyService<S>;
|
||||
|
||||
fn layer(&self, inner: S) -> Self::Service {
|
||||
MyService {
|
||||
inner,
|
||||
state: self.state.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct MyService<S> {
|
||||
inner: S,
|
||||
state: AppState,
|
||||
}
|
||||
|
||||
impl<S, B> Service<Request<B>> for MyService<S>
|
||||
where
|
||||
S: Service<Request<B>>,
|
||||
{
|
||||
type Response = S::Response;
|
||||
type Error = S::Error;
|
||||
type Future = S::Future;
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.inner.poll_ready(cx)
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request<B>) -> Self::Future {
|
||||
// do something with `self.state`
|
||||
|
||||
self.inner.call(req)
|
||||
}
|
||||
}
|
||||
|
||||
async fn handler(_: State<AppState>) {}
|
||||
|
||||
let state = AppState {};
|
||||
|
||||
let app = Router::with_state(state.clone())
|
||||
.route("/", get(handler))
|
||||
.layer(MyLayer { state });
|
||||
# let _: Router<_> = app;
|
||||
```
|
||||
|
||||
# Passing state from middleware to handlers
|
||||
|
||||
State can be passed from middleware to handlers using [request extensions]:
|
||||
|
||||
```rust
|
||||
use axum::{
|
||||
@@ -415,6 +526,8 @@ async fn auth<B>(mut req: Request<B>, next: Next<B>) -> Result<Response, StatusC
|
||||
};
|
||||
|
||||
if let Some(current_user) = authorize_current_user(auth_header).await {
|
||||
// insert the current user into a request extension so the handler can
|
||||
// extract it
|
||||
req.extensions_mut().insert(current_user);
|
||||
Ok(next.run(req).await)
|
||||
} else {
|
||||
@@ -437,7 +550,7 @@ async fn handler(
|
||||
let app = Router::new()
|
||||
.route("/", get(handler))
|
||||
.route_layer(middleware::from_fn(auth));
|
||||
# let app: Router = app;
|
||||
# let _: Router<()> = app;
|
||||
```
|
||||
|
||||
[Response extensions] can also be used but note that request extensions are not
|
||||
@@ -462,3 +575,4 @@ extensions you need.
|
||||
[`MethodRouter::route_layer`]: crate::routing::MethodRouter::route_layer
|
||||
[request extensions]: https://docs.rs/http/latest/http/request/struct.Request.html#method.extensions
|
||||
[Response extensions]: https://docs.rs/http/latest/http/response/struct.Response.html#method.extensions
|
||||
[`State`]: crate::extract::State
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Add a fallback service to the router.
|
||||
Add a fallback [`Handler`] to the router.
|
||||
|
||||
This service will be called if no routes matches the incoming request.
|
||||
|
||||
@@ -13,7 +13,7 @@ use axum::{
|
||||
|
||||
let app = Router::new()
|
||||
.route("/foo", get(|| async { /* ... */ }))
|
||||
.fallback(fallback.into_service());
|
||||
.fallback(fallback);
|
||||
|
||||
async fn fallback(uri: Uri) -> (StatusCode, String) {
|
||||
(StatusCode::NOT_FOUND, format!("No route for {}", uri))
|
||||
|
||||
@@ -104,7 +104,7 @@ let api_routes = Router::new().nest("/users", get(|| async {}));
|
||||
|
||||
let app = Router::new()
|
||||
.nest("/api", api_routes)
|
||||
.fallback(fallback.into_service());
|
||||
.fallback(fallback);
|
||||
# let _: Router = app;
|
||||
```
|
||||
|
||||
@@ -132,11 +132,11 @@ async fn api_fallback() -> (StatusCode, Json<Value>) {
|
||||
let api_routes = Router::new()
|
||||
.nest("/users", get(|| async {}))
|
||||
// add dedicated fallback for requests starting with `/api`
|
||||
.fallback(api_fallback.into_service());
|
||||
.fallback(api_fallback);
|
||||
|
||||
let app = Router::new()
|
||||
.nest("/api", api_routes)
|
||||
.fallback(fallback.into_service());
|
||||
.fallback(fallback);
|
||||
# let _: Router = app;
|
||||
```
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@ Add another route to the router.
|
||||
`path` is a string of path segments separated by `/`. Each segment
|
||||
can be either static, a capture, or a wildcard.
|
||||
|
||||
`service` is the [`Service`] that should receive the request if the path matches
|
||||
`path`. `service` will commonly be a handler wrapped in a method router like
|
||||
[`get`](crate::routing::get). See [`handler`](crate::handler) for more details
|
||||
on handlers.
|
||||
`method_router` is the [`MethodRouter`] that should receive the request if the
|
||||
path matches `path`. `method_router` will commonly be a handler wrapped in a method
|
||||
router like [`get`](crate::routing::get). See [`handler`](crate::handler) for
|
||||
more details on handlers.
|
||||
|
||||
# Static paths
|
||||
|
||||
@@ -105,69 +105,6 @@ async fn serve_asset(Path(path): Path<String>) {}
|
||||
# };
|
||||
```
|
||||
|
||||
# Routing to any [`Service`]
|
||||
|
||||
axum also supports routing to general [`Service`]s:
|
||||
|
||||
```rust,no_run
|
||||
use axum::{
|
||||
Router,
|
||||
body::Body,
|
||||
routing::{any_service, get_service},
|
||||
http::{Request, StatusCode},
|
||||
error_handling::HandleErrorLayer,
|
||||
};
|
||||
use tower_http::services::ServeFile;
|
||||
use http::Response;
|
||||
use std::{convert::Infallible, io};
|
||||
use tower::service_fn;
|
||||
|
||||
let app = Router::new()
|
||||
.route(
|
||||
// Any request to `/` goes to a service
|
||||
"/",
|
||||
// Services whose response body is not `axum::body::BoxBody`
|
||||
// can be wrapped in `axum::routing::any_service` (or one of the other routing filters)
|
||||
// to have the response body mapped
|
||||
any_service(service_fn(|_: Request<Body>| async {
|
||||
let res = Response::new(Body::from("Hi from `GET /`"));
|
||||
Ok::<_, Infallible>(res)
|
||||
}))
|
||||
)
|
||||
.route(
|
||||
"/foo",
|
||||
// This service's response body is `axum::body::BoxBody` so
|
||||
// it can be routed to directly.
|
||||
service_fn(|req: Request<Body>| async move {
|
||||
let body = Body::from(format!("Hi from `{} /foo`", req.method()));
|
||||
let body = axum::body::boxed(body);
|
||||
let res = Response::new(body);
|
||||
Ok::<_, Infallible>(res)
|
||||
})
|
||||
)
|
||||
.route(
|
||||
// GET `/static/Cargo.toml` goes to a service from tower-http
|
||||
"/static/Cargo.toml",
|
||||
get_service(ServeFile::new("Cargo.toml"))
|
||||
// though we must handle any potential errors
|
||||
.handle_error(|error: io::Error| async move {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Unhandled internal error: {}", error),
|
||||
)
|
||||
})
|
||||
);
|
||||
# async {
|
||||
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
# };
|
||||
```
|
||||
|
||||
Routing to arbitrary services in this way has complications for backpressure
|
||||
([`Service::poll_ready`]). See the [Routing to services and backpressure] module
|
||||
for more details.
|
||||
|
||||
[Routing to services and backpressure]: middleware/index.html#routing-to-servicesmiddleware-and-backpressure
|
||||
|
||||
# Panics
|
||||
|
||||
Panics if the route overlaps with another route:
|
||||
@@ -187,21 +124,3 @@ The static route `/foo` and the dynamic route `/:key` are not considered to
|
||||
overlap and `/foo` will take precedence.
|
||||
|
||||
Also panics if `path` is empty.
|
||||
|
||||
## Nesting
|
||||
|
||||
`route` cannot be used to nest `Router`s. Instead use [`Router::nest`].
|
||||
|
||||
Attempting to will result in a panic:
|
||||
|
||||
```rust,should_panic
|
||||
use axum::{routing::get, Router};
|
||||
|
||||
let app = Router::new().route(
|
||||
"/",
|
||||
Router::new().route("/foo", get(|| async {})),
|
||||
);
|
||||
# async {
|
||||
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
# };
|
||||
```
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
Add another route to the router that calls a [`Service`].
|
||||
|
||||
# Example
|
||||
|
||||
```rust,no_run
|
||||
use axum::{
|
||||
Router,
|
||||
body::Body,
|
||||
routing::{any_service, get_service},
|
||||
http::{Request, StatusCode},
|
||||
error_handling::HandleErrorLayer,
|
||||
};
|
||||
use tower_http::services::ServeFile;
|
||||
use http::Response;
|
||||
use std::{convert::Infallible, io};
|
||||
use tower::service_fn;
|
||||
|
||||
let app = Router::new()
|
||||
.route(
|
||||
// Any request to `/` goes to a service
|
||||
"/",
|
||||
// Services whose response body is not `axum::body::BoxBody`
|
||||
// can be wrapped in `axum::routing::any_service` (or one of the other routing filters)
|
||||
// to have the response body mapped
|
||||
any_service(service_fn(|_: Request<Body>| async {
|
||||
let res = Response::new(Body::from("Hi from `GET /`"));
|
||||
Ok::<_, Infallible>(res)
|
||||
}))
|
||||
)
|
||||
.route_service(
|
||||
"/foo",
|
||||
// This service's response body is `axum::body::BoxBody` so
|
||||
// it can be routed to directly.
|
||||
service_fn(|req: Request<Body>| async move {
|
||||
let body = Body::from(format!("Hi from `{} /foo`", req.method()));
|
||||
let body = axum::body::boxed(body);
|
||||
let res = Response::new(body);
|
||||
Ok::<_, Infallible>(res)
|
||||
})
|
||||
)
|
||||
.route(
|
||||
// GET `/static/Cargo.toml` goes to a service from tower-http
|
||||
"/static/Cargo.toml",
|
||||
get_service(ServeFile::new("Cargo.toml"))
|
||||
// though we must handle any potential errors
|
||||
.handle_error(|error: io::Error| async move {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Unhandled internal error: {}", error),
|
||||
)
|
||||
})
|
||||
);
|
||||
# async {
|
||||
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
# };
|
||||
```
|
||||
|
||||
Routing to arbitrary services in this way has complications for backpressure
|
||||
([`Service::poll_ready`]). See the [Routing to services and backpressure] module
|
||||
for more details.
|
||||
|
||||
# Panics
|
||||
|
||||
Panics for the same reasons as [`Router::route`] or if you attempt to route to a
|
||||
`Router`:
|
||||
|
||||
```rust,should_panic
|
||||
use axum::{routing::get, Router};
|
||||
|
||||
let app = Router::new().route_service(
|
||||
"/",
|
||||
Router::new().route("/foo", get(|| async {})),
|
||||
);
|
||||
# async {
|
||||
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
# };
|
||||
```
|
||||
|
||||
Use [`Router::nest`] instead.
|
||||
|
||||
[Routing to services and backpressure]: middleware/index.html#routing-to-servicesmiddleware-and-backpressure
|
||||
@@ -1,7 +1,6 @@
|
||||
#![doc = include_str!("../docs/error_handling.md")]
|
||||
|
||||
use crate::{
|
||||
body::boxed,
|
||||
extract::{FromRequest, RequestParts},
|
||||
http::{Request, StatusCode},
|
||||
response::{IntoResponse, Response},
|
||||
@@ -113,16 +112,16 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, F, ReqBody, Fut, Res> Service<Request<ReqBody>> for HandleError<S, F, ()>
|
||||
impl<S, F, B, Fut, Res> Service<Request<B>> for HandleError<S, F, ()>
|
||||
where
|
||||
S: Service<Request<ReqBody>> + Clone + Send + 'static,
|
||||
S: Service<Request<B>> + Clone + Send + 'static,
|
||||
S::Response: IntoResponse + Send,
|
||||
S::Error: Send,
|
||||
S::Future: Send,
|
||||
F: FnOnce(S::Error) -> Fut + Clone + Send + 'static,
|
||||
Fut: Future<Output = Res> + Send,
|
||||
Res: IntoResponse,
|
||||
ReqBody: Send + 'static,
|
||||
B: Send + 'static,
|
||||
{
|
||||
type Response = Response;
|
||||
type Error = Infallible;
|
||||
@@ -132,7 +131,7 @@ where
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request<ReqBody>) -> Self::Future {
|
||||
fn call(&mut self, req: Request<B>) -> Self::Future {
|
||||
let f = self.f.clone();
|
||||
|
||||
let clone = self.inner.clone();
|
||||
@@ -152,18 +151,18 @@ where
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! impl_service {
|
||||
( $($ty:ident),* $(,)? ) => {
|
||||
impl<S, F, ReqBody, Res, Fut, $($ty,)*> Service<Request<ReqBody>>
|
||||
impl<S, F, B, Res, Fut, $($ty,)*> Service<Request<B>>
|
||||
for HandleError<S, F, ($($ty,)*)>
|
||||
where
|
||||
S: Service<Request<ReqBody>> + Clone + Send + 'static,
|
||||
S: Service<Request<B>> + Clone + Send + 'static,
|
||||
S::Response: IntoResponse + Send,
|
||||
S::Error: Send,
|
||||
S::Future: Send,
|
||||
F: FnOnce($($ty),*, S::Error) -> Fut + Clone + Send + 'static,
|
||||
Fut: Future<Output = Res> + Send,
|
||||
Res: IntoResponse,
|
||||
$( $ty: FromRequest<ReqBody> + Send,)*
|
||||
ReqBody: Send + 'static,
|
||||
$( $ty: FromRequest<(), B> + Send,)*
|
||||
B: Send + 'static,
|
||||
{
|
||||
type Response = Response;
|
||||
type Error = Infallible;
|
||||
@@ -175,7 +174,7 @@ macro_rules! impl_service {
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn call(&mut self, req: Request<ReqBody>) -> Self::Future {
|
||||
fn call(&mut self, req: Request<B>) -> Self::Future {
|
||||
let f = self.f.clone();
|
||||
|
||||
let clone = self.inner.clone();
|
||||
@@ -187,7 +186,7 @@ macro_rules! impl_service {
|
||||
$(
|
||||
let $ty = match $ty::from_request(&mut req).await {
|
||||
Ok(value) => value,
|
||||
Err(rejection) => return Ok(rejection.into_response().map(boxed)),
|
||||
Err(rejection) => return Ok(rejection.into_response()),
|
||||
};
|
||||
)*
|
||||
|
||||
@@ -200,7 +199,7 @@ macro_rules! impl_service {
|
||||
|
||||
match inner.oneshot(req).await {
|
||||
Ok(res) => Ok(res.into_response()),
|
||||
Err(err) => Ok(f($($ty),*, err).await.into_response().map(boxed)),
|
||||
Err(err) => Ok(f($($ty),*, err).await.into_response()),
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -73,14 +73,15 @@ use tower_service::Service;
|
||||
pub struct Extension<T>(pub T);
|
||||
|
||||
#[async_trait]
|
||||
impl<T, B> FromRequest<B> for Extension<T>
|
||||
impl<T, S, B> FromRequest<S, B> for Extension<T>
|
||||
where
|
||||
T: Clone + Send + Sync + 'static,
|
||||
B: Send,
|
||||
S: Send,
|
||||
{
|
||||
type Rejection = ExtensionRejection;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
let value = req
|
||||
.extensions()
|
||||
.get::<T>()
|
||||
|
||||
@@ -128,14 +128,15 @@ opaque_future! {
|
||||
pub struct ConnectInfo<T>(pub T);
|
||||
|
||||
#[async_trait]
|
||||
impl<B, T> FromRequest<B> for ConnectInfo<T>
|
||||
impl<S, B, T> FromRequest<S, B> for ConnectInfo<T>
|
||||
where
|
||||
B: Send,
|
||||
S: Send,
|
||||
T: Clone + Send + Sync + 'static,
|
||||
{
|
||||
type Rejection = <Extension<Self> as FromRequest<B>>::Rejection;
|
||||
type Rejection = <Extension<Self> as FromRequest<S, B>>::Rejection;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
let Extension(connect_info) = Extension::<Self>::from_request(req).await?;
|
||||
Ok(connect_info)
|
||||
}
|
||||
|
||||
@@ -36,15 +36,16 @@ use std::ops::Deref;
|
||||
pub struct ContentLengthLimit<T, const N: u64>(pub T);
|
||||
|
||||
#[async_trait]
|
||||
impl<T, B, const N: u64> FromRequest<B> for ContentLengthLimit<T, N>
|
||||
impl<T, S, B, const N: u64> FromRequest<S, B> for ContentLengthLimit<T, N>
|
||||
where
|
||||
T: FromRequest<B>,
|
||||
T: FromRequest<S, B>,
|
||||
T::Rejection: IntoResponse,
|
||||
B: Send,
|
||||
S: Send,
|
||||
{
|
||||
type Rejection = ContentLengthLimitRejection<T::Rejection>;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
let content_length = req
|
||||
.headers()
|
||||
.get(http::header::CONTENT_LENGTH)
|
||||
|
||||
@@ -21,13 +21,14 @@ const X_FORWARDED_HOST_HEADER_KEY: &str = "X-Forwarded-Host";
|
||||
pub struct Host(pub String);
|
||||
|
||||
#[async_trait]
|
||||
impl<B> FromRequest<B> for Host
|
||||
impl<S, B> FromRequest<S, B> for Host
|
||||
where
|
||||
B: Send,
|
||||
S: Send,
|
||||
{
|
||||
type Rejection = HostRejection;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
if let Some(host) = parse_forwarded(req.headers()) {
|
||||
return Ok(Host(host.to_owned()));
|
||||
}
|
||||
|
||||
@@ -64,13 +64,14 @@ impl MatchedPath {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<B> FromRequest<B> for MatchedPath
|
||||
impl<S, B> FromRequest<S, B> for MatchedPath
|
||||
where
|
||||
B: Send,
|
||||
S: Send,
|
||||
{
|
||||
type Rejection = MatchedPathRejection;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
let matched_path = req
|
||||
.extensions()
|
||||
.get::<Self>()
|
||||
@@ -84,7 +85,9 @@ where
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{extract::Extension, handler::Handler, routing::get, test_helpers::*, Router};
|
||||
use crate::{
|
||||
extract::Extension, handler::HandlerWithoutStateExt, routing::get, test_helpers::*, Router,
|
||||
};
|
||||
use http::{Request, StatusCode};
|
||||
use std::task::{Context, Poll};
|
||||
use tower::layer::layer_fn;
|
||||
@@ -93,7 +96,7 @@ mod tests {
|
||||
#[derive(Clone)]
|
||||
struct SetMatchedPathExtension<S>(S);
|
||||
|
||||
impl<B, S> Service<Request<B>> for SetMatchedPathExtension<S>
|
||||
impl<S, B> Service<Request<B>> for SetMatchedPathExtension<S>
|
||||
where
|
||||
S: Service<Request<B>>,
|
||||
{
|
||||
|
||||
@@ -14,9 +14,10 @@ mod content_length_limit;
|
||||
mod host;
|
||||
mod raw_query;
|
||||
mod request_parts;
|
||||
mod state;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use axum_core::extract::{FromRequest, RequestParts};
|
||||
pub use axum_core::extract::{FromRef, FromRequest, RequestParts};
|
||||
|
||||
#[doc(inline)]
|
||||
#[allow(deprecated)]
|
||||
@@ -27,6 +28,7 @@ pub use self::{
|
||||
path::Path,
|
||||
raw_query::RawQuery,
|
||||
request_parts::{BodyStream, RawBody},
|
||||
state::State,
|
||||
};
|
||||
|
||||
#[doc(no_inline)]
|
||||
@@ -73,13 +75,13 @@ pub use self::ws::WebSocketUpgrade;
|
||||
#[doc(no_inline)]
|
||||
pub use crate::TypedHeader;
|
||||
|
||||
pub(crate) fn take_body<B>(req: &mut RequestParts<B>) -> Result<B, BodyAlreadyExtracted> {
|
||||
pub(crate) fn take_body<S, B>(req: &mut RequestParts<S, B>) -> Result<B, BodyAlreadyExtracted> {
|
||||
req.take_body().ok_or_else(BodyAlreadyExtracted::default)
|
||||
}
|
||||
|
||||
// this is duplicated in `axum-extra/src/extract/form.rs`
|
||||
pub(super) fn has_content_type<B>(
|
||||
req: &RequestParts<B>,
|
||||
pub(super) fn has_content_type<S, B>(
|
||||
req: &RequestParts<S, B>,
|
||||
expected_content_type: &mime::Mime,
|
||||
) -> bool {
|
||||
let content_type = if let Some(content_type) = req.headers().get(header::CONTENT_TYPE) {
|
||||
|
||||
@@ -50,14 +50,15 @@ pub struct Multipart {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<B> FromRequest<B> for Multipart
|
||||
impl<S, B> FromRequest<S, B> for Multipart
|
||||
where
|
||||
B: HttpBody<Data = Bytes> + Default + Unpin + Send + 'static,
|
||||
B::Error: Into<BoxError>,
|
||||
S: Send,
|
||||
{
|
||||
type Rejection = MultipartRejection;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
let stream = BodyStream::from_request(req).await?;
|
||||
let headers = req.headers();
|
||||
let boundary = parse_boundary(headers).ok_or(InvalidBoundary)?;
|
||||
@@ -179,7 +180,7 @@ impl<'a> Field<'a> {
|
||||
/// }
|
||||
///
|
||||
/// let app = Router::new().route("/upload", post(upload));
|
||||
/// # let _: Router<axum::body::Body> = app;
|
||||
/// # let _: Router = app;
|
||||
/// ```
|
||||
pub async fn chunk(&mut self) -> Result<Option<Bytes>, MultipartError> {
|
||||
self.inner
|
||||
|
||||
@@ -163,14 +163,15 @@ impl<T> DerefMut for Path<T> {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T, B> FromRequest<B> for Path<T>
|
||||
impl<T, S, B> FromRequest<S, B> for Path<T>
|
||||
where
|
||||
T: DeserializeOwned + Send,
|
||||
B: Send,
|
||||
S: Send,
|
||||
{
|
||||
type Rejection = PathRejection;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
let params = match req.extensions_mut().get::<UrlParams>() {
|
||||
Some(UrlParams::Params(params)) => params,
|
||||
Some(UrlParams::InvalidUtf8InPathParam { key }) => {
|
||||
|
||||
@@ -49,14 +49,15 @@ use std::ops::Deref;
|
||||
pub struct Query<T>(pub T);
|
||||
|
||||
#[async_trait]
|
||||
impl<T, B> FromRequest<B> for Query<T>
|
||||
impl<T, S, B> FromRequest<S, B> for Query<T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
B: Send,
|
||||
S: Send,
|
||||
{
|
||||
type Rejection = QueryRejection;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
let query = req.uri().query().unwrap_or_default();
|
||||
let value = serde_urlencoded::from_str(query)
|
||||
.map_err(FailedToDeserializeQueryString::__private_new)?;
|
||||
@@ -81,7 +82,8 @@ mod tests {
|
||||
use std::fmt::Debug;
|
||||
|
||||
async fn check<T: DeserializeOwned + PartialEq + Debug>(uri: impl AsRef<str>, value: T) {
|
||||
let mut req = RequestParts::new(Request::builder().uri(uri.as_ref()).body(()).unwrap());
|
||||
let req = Request::builder().uri(uri.as_ref()).body(()).unwrap();
|
||||
let mut req = RequestParts::new(req);
|
||||
assert_eq!(Query::<T>::from_request(&mut req).await.unwrap().0, value);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,13 +27,14 @@ use std::convert::Infallible;
|
||||
pub struct RawQuery(pub Option<String>);
|
||||
|
||||
#[async_trait]
|
||||
impl<B> FromRequest<B> for RawQuery
|
||||
impl<S, B> FromRequest<S, B> for RawQuery
|
||||
where
|
||||
B: Send,
|
||||
S: Send,
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
let query = req.uri().query().map(|query| query.to_owned());
|
||||
Ok(Self(query))
|
||||
}
|
||||
|
||||
@@ -86,13 +86,14 @@ pub struct OriginalUri(pub Uri);
|
||||
|
||||
#[cfg(feature = "original-uri")]
|
||||
#[async_trait]
|
||||
impl<B> FromRequest<B> for OriginalUri
|
||||
impl<S, B> FromRequest<S, B> for OriginalUri
|
||||
where
|
||||
B: Send,
|
||||
S: Send,
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
let uri = Extension::<Self>::from_request(req)
|
||||
.await
|
||||
.unwrap_or_else(|_| Extension(OriginalUri(req.uri().clone())))
|
||||
@@ -140,15 +141,16 @@ impl Stream for BodyStream {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<B> FromRequest<B> for BodyStream
|
||||
impl<S, B> FromRequest<S, B> for BodyStream
|
||||
where
|
||||
B: HttpBody + Send + 'static,
|
||||
B::Data: Into<Bytes>,
|
||||
B::Error: Into<BoxError>,
|
||||
S: Send,
|
||||
{
|
||||
type Rejection = BodyAlreadyExtracted;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
let body = take_body(req)?
|
||||
.map_data(Into::into)
|
||||
.map_err(|err| Error::new(err.into()));
|
||||
@@ -196,13 +198,14 @@ fn body_stream_traits() {
|
||||
pub struct RawBody<B = Body>(pub B);
|
||||
|
||||
#[async_trait]
|
||||
impl<B> FromRequest<B> for RawBody<B>
|
||||
impl<S, B> FromRequest<S, B> for RawBody<B>
|
||||
where
|
||||
B: Send,
|
||||
S: Send,
|
||||
{
|
||||
type Rejection = BodyAlreadyExtracted;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
let body = take_body(req)?;
|
||||
Ok(Self(body))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
use async_trait::async_trait;
|
||||
use axum_core::extract::{FromRef, FromRequest, RequestParts};
|
||||
use std::{
|
||||
convert::Infallible,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
/// Extractor for state.
|
||||
///
|
||||
/// Note this extractor is not available to middleware. See ["Accessing state in
|
||||
/// middleware"][state-from-middleware] for how to access state in middleware.
|
||||
///
|
||||
/// [state-from-middleware]: ../middleware/index.html#accessing-state-in-middleware
|
||||
///
|
||||
/// # With `Router`
|
||||
///
|
||||
/// ```
|
||||
/// use axum::{Router, routing::get, extract::State};
|
||||
///
|
||||
/// // the application state
|
||||
/// //
|
||||
/// // here you can put configuration, database connection pools, or whatever
|
||||
/// // state you need
|
||||
/// #[derive(Clone)]
|
||||
/// struct AppState {}
|
||||
///
|
||||
/// let state = AppState {};
|
||||
///
|
||||
/// // create a `Router` that holds our state
|
||||
/// let app = Router::with_state(state).route("/", get(handler));
|
||||
///
|
||||
/// async fn handler(
|
||||
/// // access the state via the `State` extractor
|
||||
/// // extracting a state of the wrong type results in a compile error
|
||||
/// State(state): State<AppState>,
|
||||
/// ) {
|
||||
/// // use `state`...
|
||||
/// }
|
||||
/// # let _: Router<AppState> = app;
|
||||
/// ```
|
||||
///
|
||||
/// # With `MethodRouter`
|
||||
///
|
||||
/// ```
|
||||
/// use axum::{routing::get, extract::State};
|
||||
///
|
||||
/// #[derive(Clone)]
|
||||
/// struct AppState {}
|
||||
///
|
||||
/// let state = AppState {};
|
||||
///
|
||||
/// let method_router_with_state = get(handler)
|
||||
/// // provide the state so the handler can access it
|
||||
/// .with_state(state);
|
||||
///
|
||||
/// async fn handler(State(state): State<AppState>) {
|
||||
/// // use `state`...
|
||||
/// }
|
||||
/// # async {
|
||||
/// # axum::Server::bind(&"".parse().unwrap()).serve(method_router_with_state.into_make_service()).await.unwrap();
|
||||
/// # };
|
||||
/// ```
|
||||
///
|
||||
/// # With `Handler`
|
||||
///
|
||||
/// ```
|
||||
/// use axum::{routing::get, handler::Handler, extract::State};
|
||||
///
|
||||
/// #[derive(Clone)]
|
||||
/// struct AppState {}
|
||||
///
|
||||
/// let state = AppState {};
|
||||
///
|
||||
/// async fn handler(State(state): State<AppState>) {
|
||||
/// // use `state`...
|
||||
/// }
|
||||
///
|
||||
/// // provide the state so the handler can access it
|
||||
/// let handler_with_state = handler.with_state(state);
|
||||
///
|
||||
/// # async {
|
||||
/// axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
|
||||
/// .serve(handler_with_state.into_make_service())
|
||||
/// .await
|
||||
/// .expect("server failed");
|
||||
/// # };
|
||||
/// ```
|
||||
///
|
||||
/// # Substates
|
||||
///
|
||||
/// [`State`] only allows a single state type but you can use [`From`] to extract "substates":
|
||||
///
|
||||
/// ```
|
||||
/// use axum::{Router, routing::get, extract::{State, FromRef}};
|
||||
///
|
||||
/// // the application state
|
||||
/// #[derive(Clone)]
|
||||
/// struct AppState {
|
||||
/// // that holds some api specific state
|
||||
/// api_state: ApiState,
|
||||
/// }
|
||||
///
|
||||
/// // the api specific state
|
||||
/// #[derive(Clone)]
|
||||
/// struct ApiState {}
|
||||
///
|
||||
/// // support converting an `AppState` in an `ApiState`
|
||||
/// impl FromRef<AppState> for ApiState {
|
||||
/// fn from_ref(app_state: &AppState) -> ApiState {
|
||||
/// app_state.api_state.clone()
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// let state = AppState {
|
||||
/// api_state: ApiState {},
|
||||
/// };
|
||||
///
|
||||
/// let app = Router::with_state(state)
|
||||
/// .route("/", get(handler))
|
||||
/// .route("/api/users", get(api_users));
|
||||
///
|
||||
/// async fn api_users(
|
||||
/// // access the api specific state
|
||||
/// State(api_state): State<ApiState>,
|
||||
/// ) {
|
||||
/// }
|
||||
///
|
||||
/// async fn handler(
|
||||
/// // we can still access to top level state
|
||||
/// State(state): State<AppState>,
|
||||
/// ) {
|
||||
/// }
|
||||
/// # let _: Router<AppState> = app;
|
||||
/// ```
|
||||
///
|
||||
/// # For library authors
|
||||
///
|
||||
/// If you're writing a library that has an extractor that needs state, this is the recommended way
|
||||
/// to do it:
|
||||
///
|
||||
/// ```rust
|
||||
/// use axum_core::extract::{FromRequest, RequestParts, FromRef};
|
||||
/// use async_trait::async_trait;
|
||||
/// use std::convert::Infallible;
|
||||
///
|
||||
/// // the extractor your library provides
|
||||
/// struct MyLibraryExtractor;
|
||||
///
|
||||
/// #[async_trait]
|
||||
/// impl<S, B> FromRequest<S, B> for MyLibraryExtractor
|
||||
/// where
|
||||
/// B: Send,
|
||||
/// // keep `S` generic but require that it can produce a `MyLibraryState`
|
||||
/// // this means users will have to implement `FromRef<UserState> for MyLibraryState`
|
||||
/// MyLibraryState: FromRef<S>,
|
||||
/// S: Send,
|
||||
/// {
|
||||
/// type Rejection = Infallible;
|
||||
///
|
||||
/// async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
/// // get a `MyLibraryState` from a reference to the state
|
||||
/// let state = MyLibraryState::from_ref(req.state());
|
||||
///
|
||||
/// // ...
|
||||
/// # todo!()
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// // the state your library needs
|
||||
/// struct MyLibraryState {
|
||||
/// // ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Note that you don't need to use the `State` extractor since you can access the state directly
|
||||
/// from [`RequestParts`].
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub struct State<S>(pub S);
|
||||
|
||||
#[async_trait]
|
||||
impl<B, OuterState, InnerState> FromRequest<OuterState, B> for State<InnerState>
|
||||
where
|
||||
B: Send,
|
||||
InnerState: FromRef<OuterState>,
|
||||
OuterState: Send,
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<OuterState, B>) -> Result<Self, Self::Rejection> {
|
||||
let inner_state = InnerState::from_ref(req.state());
|
||||
Ok(Self(inner_state))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Deref for State<S> {
|
||||
type Target = S;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> DerefMut for State<S> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
@@ -275,13 +275,14 @@ impl WebSocketUpgrade {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<B> FromRequest<B> for WebSocketUpgrade
|
||||
impl<S, B> FromRequest<S, B> for WebSocketUpgrade
|
||||
where
|
||||
B: Send,
|
||||
S: Send,
|
||||
{
|
||||
type Rejection = WebSocketUpgradeRejection;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
if req.method() != Method::GET {
|
||||
return Err(MethodNotGet.into());
|
||||
}
|
||||
@@ -320,7 +321,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn header_eq<B>(req: &RequestParts<B>, key: HeaderName, value: &'static str) -> bool {
|
||||
fn header_eq<S, B>(req: &RequestParts<S, B>, key: HeaderName, value: &'static str) -> bool {
|
||||
if let Some(header) = req.headers().get(&key) {
|
||||
header.as_bytes().eq_ignore_ascii_case(value.as_bytes())
|
||||
} else {
|
||||
@@ -328,7 +329,7 @@ fn header_eq<B>(req: &RequestParts<B>, key: HeaderName, value: &'static str) ->
|
||||
}
|
||||
}
|
||||
|
||||
fn header_contains<B>(req: &RequestParts<B>, key: HeaderName, value: &'static str) -> bool {
|
||||
fn header_contains<S, B>(req: &RequestParts<S, B>, key: HeaderName, value: &'static str) -> bool {
|
||||
let header = if let Some(header) = req.headers().get(&key) {
|
||||
header
|
||||
} else {
|
||||
|
||||
+34
-36
@@ -56,16 +56,17 @@ use std::ops::Deref;
|
||||
pub struct Form<T>(pub T);
|
||||
|
||||
#[async_trait]
|
||||
impl<T, B> FromRequest<B> for Form<T>
|
||||
impl<T, S, B> FromRequest<S, B> for Form<T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
B: HttpBody + Send,
|
||||
B::Data: Send,
|
||||
B::Error: Into<BoxError>,
|
||||
S: Send,
|
||||
{
|
||||
type Rejection = FormRejection;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
if req.method() == Method::GET {
|
||||
let query = req.uri().query().unwrap_or_default();
|
||||
let value = serde_urlencoded::from_str(query)
|
||||
@@ -125,29 +126,27 @@ mod tests {
|
||||
}
|
||||
|
||||
async fn check_query<T: DeserializeOwned + PartialEq + Debug>(uri: impl AsRef<str>, value: T) {
|
||||
let mut req = RequestParts::new(
|
||||
Request::builder()
|
||||
.uri(uri.as_ref())
|
||||
.body(Empty::<Bytes>::new())
|
||||
.unwrap(),
|
||||
);
|
||||
let req = Request::builder()
|
||||
.uri(uri.as_ref())
|
||||
.body(Empty::<Bytes>::new())
|
||||
.unwrap();
|
||||
let mut req = RequestParts::new(req);
|
||||
assert_eq!(Form::<T>::from_request(&mut req).await.unwrap().0, value);
|
||||
}
|
||||
|
||||
async fn check_body<T: Serialize + DeserializeOwned + PartialEq + Debug>(value: T) {
|
||||
let mut req = RequestParts::new(
|
||||
Request::builder()
|
||||
.uri("http://example.com/test")
|
||||
.method(Method::POST)
|
||||
.header(
|
||||
http::header::CONTENT_TYPE,
|
||||
mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(),
|
||||
)
|
||||
.body(Full::<Bytes>::new(
|
||||
serde_urlencoded::to_string(&value).unwrap().into(),
|
||||
))
|
||||
.unwrap(),
|
||||
);
|
||||
let req = Request::builder()
|
||||
.uri("http://example.com/test")
|
||||
.method(Method::POST)
|
||||
.header(
|
||||
http::header::CONTENT_TYPE,
|
||||
mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(),
|
||||
)
|
||||
.body(Full::<Bytes>::new(
|
||||
serde_urlencoded::to_string(&value).unwrap().into(),
|
||||
))
|
||||
.unwrap();
|
||||
let mut req = RequestParts::new(req);
|
||||
assert_eq!(Form::<T>::from_request(&mut req).await.unwrap().0, value);
|
||||
}
|
||||
|
||||
@@ -204,21 +203,20 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_incorrect_content_type() {
|
||||
let mut req = RequestParts::new(
|
||||
Request::builder()
|
||||
.uri("http://example.com/test")
|
||||
.method(Method::POST)
|
||||
.header(http::header::CONTENT_TYPE, mime::APPLICATION_JSON.as_ref())
|
||||
.body(Full::<Bytes>::new(
|
||||
serde_urlencoded::to_string(&Pagination {
|
||||
size: Some(10),
|
||||
page: None,
|
||||
})
|
||||
.unwrap()
|
||||
.into(),
|
||||
))
|
||||
.unwrap(),
|
||||
);
|
||||
let req = Request::builder()
|
||||
.uri("http://example.com/test")
|
||||
.method(Method::POST)
|
||||
.header(http::header::CONTENT_TYPE, mime::APPLICATION_JSON.as_ref())
|
||||
.body(Full::<Bytes>::new(
|
||||
serde_urlencoded::to_string(&Pagination {
|
||||
size: Some(10),
|
||||
page: None,
|
||||
})
|
||||
.unwrap()
|
||||
.into(),
|
||||
))
|
||||
.unwrap();
|
||||
let mut req = RequestParts::new(req);
|
||||
assert!(matches!(
|
||||
Form::<Pagination>::from_request(&mut req)
|
||||
.await
|
||||
|
||||
@@ -19,29 +19,29 @@ opaque_future! {
|
||||
|
||||
pin_project! {
|
||||
/// The response future for [`Layered`](super::Layered).
|
||||
pub struct LayeredFuture<S, ReqBody>
|
||||
pub struct LayeredFuture<B, S>
|
||||
where
|
||||
S: Service<Request<ReqBody>>,
|
||||
S: Service<Request<B>>,
|
||||
{
|
||||
#[pin]
|
||||
inner: Map<Oneshot<S, Request<ReqBody>>, fn(Result<S::Response, S::Error>) -> Response>,
|
||||
inner: Map<Oneshot<S, Request<B>>, fn(Result<S::Response, S::Error>) -> Response>,
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, ReqBody> LayeredFuture<S, ReqBody>
|
||||
impl<B, S> LayeredFuture<B, S>
|
||||
where
|
||||
S: Service<Request<ReqBody>>,
|
||||
S: Service<Request<B>>,
|
||||
{
|
||||
pub(super) fn new(
|
||||
inner: Map<Oneshot<S, Request<ReqBody>>, fn(Result<S::Response, S::Error>) -> Response>,
|
||||
inner: Map<Oneshot<S, Request<B>>, fn(Result<S::Response, S::Error>) -> Response>,
|
||||
) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, ReqBody> Future for LayeredFuture<S, ReqBody>
|
||||
impl<B, S> Future for LayeredFuture<B, S>
|
||||
where
|
||||
S: Service<Request<ReqBody>>,
|
||||
S: Service<Request<B>>,
|
||||
{
|
||||
type Output = Response;
|
||||
|
||||
|
||||
@@ -11,29 +11,40 @@ use tower_service::Service;
|
||||
|
||||
/// An adapter that makes a [`Handler`] into a [`Service`].
|
||||
///
|
||||
/// Created with [`Handler::into_service`].
|
||||
pub struct IntoService<H, T, B> {
|
||||
/// Created with [`HandlerWithoutStateExt::into_service`].
|
||||
///
|
||||
/// [`HandlerWithoutStateExt::into_service`]: super::HandlerWithoutStateExt::into_service
|
||||
pub struct IntoService<H, T, S, B> {
|
||||
handler: H,
|
||||
state: S,
|
||||
_marker: PhantomData<fn() -> (T, B)>,
|
||||
}
|
||||
|
||||
impl<H, T, S, B> IntoService<H, T, S, B> {
|
||||
/// Get a reference to the state.
|
||||
pub fn state(&self) -> &S {
|
||||
&self.state
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn traits() {
|
||||
use crate::test_helpers::*;
|
||||
assert_send::<IntoService<(), NotSendSync, NotSendSync>>();
|
||||
assert_sync::<IntoService<(), NotSendSync, NotSendSync>>();
|
||||
assert_send::<IntoService<(), NotSendSync, (), NotSendSync>>();
|
||||
assert_sync::<IntoService<(), NotSendSync, (), NotSendSync>>();
|
||||
}
|
||||
|
||||
impl<H, T, B> IntoService<H, T, B> {
|
||||
pub(super) fn new(handler: H) -> Self {
|
||||
impl<H, T, S, B> IntoService<H, T, S, B> {
|
||||
pub(super) fn new(handler: H, state: S) -> Self {
|
||||
Self {
|
||||
handler,
|
||||
state,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, T, B> fmt::Debug for IntoService<H, T, B> {
|
||||
impl<H, T, S, B> fmt::Debug for IntoService<H, T, S, B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("IntoService")
|
||||
.field(&format_args!("..."))
|
||||
@@ -41,22 +52,25 @@ impl<H, T, B> fmt::Debug for IntoService<H, T, B> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, T, B> Clone for IntoService<H, T, B>
|
||||
impl<H, T, S, B> Clone for IntoService<H, T, S, B>
|
||||
where
|
||||
H: Clone,
|
||||
S: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
handler: self.handler.clone(),
|
||||
state: self.state.clone(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, T, B> Service<Request<B>> for IntoService<H, T, B>
|
||||
impl<H, T, S, B> Service<Request<B>> for IntoService<H, T, S, B>
|
||||
where
|
||||
H: Handler<T, B> + Clone + Send + 'static,
|
||||
H: Handler<T, S, B> + Clone + Send + 'static,
|
||||
B: Send + 'static,
|
||||
S: Clone,
|
||||
{
|
||||
type Response = Response;
|
||||
type Error = Infallible;
|
||||
@@ -74,7 +88,7 @@ where
|
||||
use futures_util::future::FutureExt;
|
||||
|
||||
let handler = self.handler.clone();
|
||||
let future = Handler::call(handler, req);
|
||||
let future = Handler::call(handler, self.state.clone(), req);
|
||||
let future = future.map(Ok as _);
|
||||
|
||||
super::future::IntoServiceFuture::new(future)
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
use super::Handler;
|
||||
use crate::response::Response;
|
||||
use http::Request;
|
||||
use std::{
|
||||
convert::Infallible,
|
||||
fmt,
|
||||
marker::PhantomData,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use tower_service::Service;
|
||||
|
||||
pub(crate) struct IntoServiceStateInExtension<H, T, S, B> {
|
||||
handler: H,
|
||||
_marker: PhantomData<fn() -> (T, S, B)>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn traits() {
|
||||
use crate::test_helpers::*;
|
||||
assert_send::<IntoServiceStateInExtension<(), NotSendSync, (), NotSendSync>>();
|
||||
assert_sync::<IntoServiceStateInExtension<(), NotSendSync, (), NotSendSync>>();
|
||||
}
|
||||
|
||||
impl<H, T, S, B> IntoServiceStateInExtension<H, T, S, B> {
|
||||
pub(crate) fn new(handler: H) -> Self {
|
||||
Self {
|
||||
handler,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, T, S, B> fmt::Debug for IntoServiceStateInExtension<H, T, S, B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("IntoServiceStateInExtension")
|
||||
.field(&format_args!("..."))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, T, S, B> Clone for IntoServiceStateInExtension<H, T, S, B>
|
||||
where
|
||||
H: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
handler: self.handler.clone(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, T, S, B> Service<Request<B>> for IntoServiceStateInExtension<H, T, S, B>
|
||||
where
|
||||
H: Handler<T, S, B> + Clone + Send + 'static,
|
||||
B: Send + 'static,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
type Response = Response;
|
||||
type Error = Infallible;
|
||||
type Future = super::future::IntoServiceFuture<H::Future>;
|
||||
|
||||
#[inline]
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
// `IntoServiceStateInExtension` can only be constructed from async functions which are always ready, or
|
||||
// from `Layered` which bufferes in `<Layered as Handler>::call` and is therefore
|
||||
// also always ready.
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, mut req: Request<B>) -> Self::Future {
|
||||
use futures_util::future::FutureExt;
|
||||
|
||||
let state = req
|
||||
.extensions_mut()
|
||||
.remove::<S>()
|
||||
.expect("state extension missing. This is a bug in axum, please file an issue");
|
||||
|
||||
let handler = self.handler.clone();
|
||||
let future = Handler::call(handler, state, req);
|
||||
let future = future.map(Ok as _);
|
||||
|
||||
super::future::IntoServiceFuture::new(future)
|
||||
}
|
||||
}
|
||||
+142
-134
@@ -49,8 +49,11 @@ use tower_service::Service;
|
||||
|
||||
pub mod future;
|
||||
mod into_service;
|
||||
mod into_service_state_in_extension;
|
||||
mod with_state;
|
||||
|
||||
pub use self::into_service::IntoService;
|
||||
pub(crate) use self::into_service_state_in_extension::IntoServiceStateInExtension;
|
||||
pub use self::{into_service::IntoService, with_state::WithState};
|
||||
|
||||
/// Trait for async functions that can be used to handle requests.
|
||||
///
|
||||
@@ -59,13 +62,45 @@ pub use self::into_service::IntoService;
|
||||
///
|
||||
/// See the [module docs](crate::handler) for more details.
|
||||
///
|
||||
/// # Converting `Handler`s into [`Service`]s
|
||||
///
|
||||
/// To convert `Handler`s into [`Service`]s you have to call either
|
||||
/// [`HandlerWithoutStateExt::into_service`] or [`Handler::with_state`]:
|
||||
///
|
||||
/// ```
|
||||
/// use tower::Service;
|
||||
/// use axum::{
|
||||
/// extract::State,
|
||||
/// body::Body,
|
||||
/// http::Request,
|
||||
/// handler::{HandlerWithoutStateExt, Handler},
|
||||
/// };
|
||||
///
|
||||
/// // this handler doesn't require any state
|
||||
/// async fn one() {}
|
||||
/// // so it can be converted to a service with `HandlerWithoutStateExt::into_service`
|
||||
/// assert_service(one.into_service());
|
||||
///
|
||||
/// // this handler requires state
|
||||
/// async fn two(_: State<String>) {}
|
||||
/// // so we have to provide it
|
||||
/// let handler_with_state = two.with_state(String::new());
|
||||
/// // which gives us a `Service`
|
||||
/// assert_service(handler_with_state);
|
||||
///
|
||||
/// // helper to check that a value implements `Service`
|
||||
/// fn assert_service<S>(service: S)
|
||||
/// where
|
||||
/// S: Service<Request<Body>>,
|
||||
/// {}
|
||||
/// ```
|
||||
#[doc = include_str!("../docs/debugging_handler_type_errors.md")]
|
||||
pub trait Handler<T, B = Body>: Clone + Send + Sized + 'static {
|
||||
pub trait Handler<T, S = (), B = Body>: 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>) -> Self::Future;
|
||||
fn call(self, state: S, req: Request<B>) -> Self::Future;
|
||||
|
||||
/// Apply a [`tower::Layer`] to the handler.
|
||||
///
|
||||
@@ -103,112 +138,26 @@ pub trait Handler<T, B = Body>: Clone + Send + Sized + 'static {
|
||||
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
/// # };
|
||||
/// ```
|
||||
fn layer<L>(self, layer: L) -> Layered<L::Service, T>
|
||||
fn layer<L>(self, layer: L) -> Layered<L, Self, T, S, B>
|
||||
where
|
||||
L: Layer<IntoService<Self, T, B>>,
|
||||
L: Layer<WithState<Self, T, S, B>>,
|
||||
{
|
||||
Layered::new(layer.layer(self.into_service()))
|
||||
Layered {
|
||||
layer,
|
||||
handler: self,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the handler into a [`Service`].
|
||||
///
|
||||
/// This is commonly used together with [`Router::fallback`]:
|
||||
///
|
||||
/// ```rust
|
||||
/// use axum::{
|
||||
/// Server,
|
||||
/// handler::Handler,
|
||||
/// http::{Uri, Method, StatusCode},
|
||||
/// response::IntoResponse,
|
||||
/// routing::{get, Router},
|
||||
/// };
|
||||
/// use tower::make::Shared;
|
||||
/// use std::net::SocketAddr;
|
||||
///
|
||||
/// async fn handler(method: Method, uri: Uri) -> (StatusCode, String) {
|
||||
/// (StatusCode::NOT_FOUND, format!("Nothing to see at {} {}", method, uri))
|
||||
/// }
|
||||
///
|
||||
/// let app = Router::new()
|
||||
/// .route("/", get(|| async {}))
|
||||
/// .fallback(handler.into_service());
|
||||
///
|
||||
/// # async {
|
||||
/// Server::bind(&SocketAddr::from(([127, 0, 0, 1], 3000)))
|
||||
/// .serve(app.into_make_service())
|
||||
/// .await?;
|
||||
/// # Ok::<_, hyper::Error>(())
|
||||
/// # };
|
||||
/// ```
|
||||
///
|
||||
/// [`Router::fallback`]: crate::routing::Router::fallback
|
||||
fn into_service(self) -> IntoService<Self, T, B> {
|
||||
IntoService::new(self)
|
||||
}
|
||||
|
||||
/// Convert the handler into a [`MakeService`].
|
||||
///
|
||||
/// This allows you to serve a single handler if you don't need any routing:
|
||||
///
|
||||
/// ```rust
|
||||
/// use axum::{
|
||||
/// Server, handler::Handler, http::{Uri, Method}, response::IntoResponse,
|
||||
/// };
|
||||
/// use std::net::SocketAddr;
|
||||
///
|
||||
/// async fn handler(method: Method, uri: Uri, body: String) -> String {
|
||||
/// format!("received `{} {}` with body `{:?}`", method, uri, body)
|
||||
/// }
|
||||
///
|
||||
/// # async {
|
||||
/// Server::bind(&SocketAddr::from(([127, 0, 0, 1], 3000)))
|
||||
/// .serve(handler.into_make_service())
|
||||
/// .await?;
|
||||
/// # Ok::<_, hyper::Error>(())
|
||||
/// # };
|
||||
/// ```
|
||||
///
|
||||
/// [`MakeService`]: tower::make::MakeService
|
||||
fn into_make_service(self) -> IntoMakeService<IntoService<Self, T, B>> {
|
||||
IntoMakeService::new(self.into_service())
|
||||
}
|
||||
|
||||
/// Convert the handler into a [`MakeService`] which stores information
|
||||
/// about the incoming connection.
|
||||
///
|
||||
/// See [`Router::into_make_service_with_connect_info`] for more details.
|
||||
///
|
||||
/// ```rust
|
||||
/// use axum::{
|
||||
/// Server,
|
||||
/// handler::Handler,
|
||||
/// response::IntoResponse,
|
||||
/// extract::ConnectInfo,
|
||||
/// };
|
||||
/// use std::net::SocketAddr;
|
||||
///
|
||||
/// async fn handler(ConnectInfo(addr): ConnectInfo<SocketAddr>) -> String {
|
||||
/// format!("Hello {}", addr)
|
||||
/// }
|
||||
///
|
||||
/// # async {
|
||||
/// Server::bind(&SocketAddr::from(([127, 0, 0, 1], 3000)))
|
||||
/// .serve(handler.into_make_service_with_connect_info::<SocketAddr>())
|
||||
/// .await?;
|
||||
/// # Ok::<_, hyper::Error>(())
|
||||
/// # };
|
||||
/// ```
|
||||
///
|
||||
/// [`MakeService`]: tower::make::MakeService
|
||||
/// [`Router::into_make_service_with_connect_info`]: crate::routing::Router::into_make_service_with_connect_info
|
||||
fn into_make_service_with_connect_info<C>(
|
||||
self,
|
||||
) -> IntoMakeServiceWithConnectInfo<IntoService<Self, T, B>, C> {
|
||||
IntoMakeServiceWithConnectInfo::new(self.into_service())
|
||||
/// Convert the handler into a [`Service`] by providing the state
|
||||
fn with_state(self, state: S) -> WithState<Self, T, S, B> {
|
||||
WithState {
|
||||
service: IntoService::new(self, state),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, Fut, Res, B> Handler<(), B> for F
|
||||
impl<F, Fut, Res, S, B> Handler<(), S, B> for F
|
||||
where
|
||||
F: FnOnce() -> Fut + Clone + Send + 'static,
|
||||
Fut: Future<Output = Res> + Send,
|
||||
@@ -217,7 +166,7 @@ where
|
||||
{
|
||||
type Future = Pin<Box<dyn Future<Output = Response> + Send>>;
|
||||
|
||||
fn call(self, _req: Request<B>) -> Self::Future {
|
||||
fn call(self, _state: S, _req: Request<B>) -> Self::Future {
|
||||
Box::pin(async move { self().await.into_response() })
|
||||
}
|
||||
}
|
||||
@@ -225,19 +174,20 @@ where
|
||||
macro_rules! impl_handler {
|
||||
( $($ty:ident),* $(,)? ) => {
|
||||
#[allow(non_snake_case)]
|
||||
impl<F, Fut, B, Res, $($ty,)*> Handler<($($ty,)*), B> for F
|
||||
impl<F, Fut, S, B, Res, $($ty,)*> Handler<($($ty,)*), S, B> for F
|
||||
where
|
||||
F: FnOnce($($ty,)*) -> Fut + Clone + Send + 'static,
|
||||
Fut: Future<Output = Res> + Send,
|
||||
B: Send + 'static,
|
||||
S: Send + 'static,
|
||||
Res: IntoResponse,
|
||||
$( $ty: FromRequest<B> + Send,)*
|
||||
$( $ty: FromRequest<S, B> + Send,)*
|
||||
{
|
||||
type Future = Pin<Box<dyn Future<Output = Response> + Send>>;
|
||||
|
||||
fn call(self, req: Request<B>) -> Self::Future {
|
||||
fn call(self, state: S, req: Request<B>) -> Self::Future {
|
||||
Box::pin(async move {
|
||||
let mut req = RequestParts::new(req);
|
||||
let mut req = RequestParts::with_state(state, req);
|
||||
|
||||
$(
|
||||
let $ty = match $ty::from_request(&mut req).await {
|
||||
@@ -260,58 +210,116 @@ 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<S, T> {
|
||||
svc: S,
|
||||
_input: PhantomData<fn() -> T>,
|
||||
pub struct Layered<L, H, T, S, B> {
|
||||
layer: L,
|
||||
handler: H,
|
||||
_marker: PhantomData<fn() -> (T, S, B)>,
|
||||
}
|
||||
|
||||
impl<S, T> fmt::Debug for Layered<S, T>
|
||||
impl<L, H, T, S, B> fmt::Debug for Layered<L, H, T, S, B>
|
||||
where
|
||||
S: fmt::Debug,
|
||||
L: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Layered").field("svc", &self.svc).finish()
|
||||
f.debug_struct("Layered")
|
||||
.field("layer", &self.layer)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, T> Clone for Layered<S, T>
|
||||
impl<L, H, T, S, B> Clone for Layered<L, H, T, S, B>
|
||||
where
|
||||
S: Clone,
|
||||
L: Clone,
|
||||
H: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self::new(self.svc.clone())
|
||||
Self {
|
||||
layer: self.layer.clone(),
|
||||
handler: self.handler.clone(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, T, ReqBody> Handler<T, ReqBody> for Layered<S, T>
|
||||
impl<H, S, T, B, L> Handler<T, S, B> for Layered<L, H, T, S, B>
|
||||
where
|
||||
S: Service<Request<ReqBody>, Error = Infallible> + Clone + Send + 'static,
|
||||
S::Response: IntoResponse,
|
||||
S::Future: Send,
|
||||
L: Layer<WithState<H, T, S, B>> + Clone + Send + 'static,
|
||||
H: Handler<T, S, B>,
|
||||
L::Service: Service<Request<B>, Error = Infallible> + Clone + Send + 'static,
|
||||
<L::Service as Service<Request<B>>>::Response: IntoResponse,
|
||||
<L::Service as Service<Request<B>>>::Future: Send,
|
||||
T: 'static,
|
||||
ReqBody: Send + 'static,
|
||||
S: 'static,
|
||||
B: Send + 'static,
|
||||
{
|
||||
type Future = future::LayeredFuture<S, ReqBody>;
|
||||
type Future = future::LayeredFuture<B, L::Service>;
|
||||
|
||||
fn call(self, req: Request<ReqBody>) -> Self::Future {
|
||||
fn call(self, state: S, req: Request<B>) -> Self::Future {
|
||||
use futures_util::future::{FutureExt, Map};
|
||||
|
||||
let future: Map<_, fn(Result<S::Response, S::Error>) -> _> =
|
||||
self.svc.oneshot(req).map(|result| match result {
|
||||
Ok(res) => res.into_response(),
|
||||
Err(err) => match err {},
|
||||
});
|
||||
let svc = self.handler.with_state(state);
|
||||
let svc = self.layer.layer(svc);
|
||||
|
||||
let future: Map<
|
||||
_,
|
||||
fn(
|
||||
Result<
|
||||
<L::Service as Service<Request<B>>>::Response,
|
||||
<L::Service as Service<Request<B>>>::Error,
|
||||
>,
|
||||
) -> _,
|
||||
> = svc.oneshot(req).map(|result| match result {
|
||||
Ok(res) => res.into_response(),
|
||||
Err(err) => match err {},
|
||||
});
|
||||
|
||||
future::LayeredFuture::new(future)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, T> Layered<S, T> {
|
||||
pub(crate) fn new(svc: S) -> Self {
|
||||
Self {
|
||||
svc,
|
||||
_input: PhantomData,
|
||||
}
|
||||
/// Extension trait for [`Handler`]s that don't have state.
|
||||
///
|
||||
/// 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> {
|
||||
/// Convert the handler into a [`Service`] and no state.
|
||||
fn into_service(self) -> WithState<Self, T, (), B>;
|
||||
|
||||
/// Convert the handler into a [`MakeService`] and no state.
|
||||
///
|
||||
/// See [`WithState::into_make_service`] for more details.
|
||||
///
|
||||
/// [`MakeService`]: tower::make::MakeService
|
||||
fn into_make_service(self) -> IntoMakeService<IntoService<Self, T, (), B>>;
|
||||
|
||||
/// Convert the handler into a [`MakeService`] which stores information
|
||||
/// about the incoming connection and has no state.
|
||||
///
|
||||
/// See [`WithState::into_make_service_with_connect_info`] for more details.
|
||||
///
|
||||
/// [`MakeService`]: tower::make::MakeService
|
||||
fn into_make_service_with_connect_info<C>(
|
||||
self,
|
||||
) -> IntoMakeServiceWithConnectInfo<IntoService<Self, T, (), B>, C>;
|
||||
}
|
||||
|
||||
impl<H, T, B> HandlerWithoutStateExt<T, B> for H
|
||||
where
|
||||
H: Handler<T, (), B>,
|
||||
{
|
||||
fn into_service(self) -> WithState<Self, T, (), B> {
|
||||
self.with_state(())
|
||||
}
|
||||
|
||||
fn into_make_service(self) -> IntoMakeService<IntoService<Self, T, (), B>> {
|
||||
self.with_state(()).into_make_service()
|
||||
}
|
||||
|
||||
fn into_make_service_with_connect_info<C>(
|
||||
self,
|
||||
) -> IntoMakeServiceWithConnectInfo<IntoService<Self, T, (), B>, C> {
|
||||
self.with_state(()).into_make_service_with_connect_info()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
use super::{Handler, IntoService};
|
||||
use crate::{extract::connect_info::IntoMakeServiceWithConnectInfo, routing::IntoMakeService};
|
||||
use http::Request;
|
||||
use std::task::{Context, Poll};
|
||||
use tower_service::Service;
|
||||
|
||||
/// A [`Handler`] which has access to some state.
|
||||
///
|
||||
/// Implements [`Service`].
|
||||
///
|
||||
/// The state can be extracted with [`State`](crate::extract::State).
|
||||
///
|
||||
/// Created with [`Handler::with_state`].
|
||||
pub struct WithState<H, T, S, B> {
|
||||
pub(super) service: IntoService<H, T, S, B>,
|
||||
}
|
||||
|
||||
impl<H, T, S, B> WithState<H, T, S, B> {
|
||||
/// Get a reference to the state.
|
||||
pub fn state(&self) -> &S {
|
||||
self.service.state()
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, T, S, B> WithState<H, T, S, B> {
|
||||
/// Convert the handler into a [`MakeService`].
|
||||
///
|
||||
/// This allows you to serve a single handler if you don't need any routing:
|
||||
///
|
||||
/// ```rust
|
||||
/// use axum::{
|
||||
/// Server,
|
||||
/// handler::Handler,
|
||||
/// extract::State,
|
||||
/// http::{Uri, Method},
|
||||
/// response::IntoResponse,
|
||||
/// };
|
||||
/// use std::net::SocketAddr;
|
||||
///
|
||||
/// #[derive(Clone)]
|
||||
/// struct AppState {}
|
||||
///
|
||||
/// async fn handler(State(state): State<AppState>) {
|
||||
/// // ...
|
||||
/// }
|
||||
///
|
||||
/// let app = handler.with_state(AppState {});
|
||||
///
|
||||
/// # async {
|
||||
/// Server::bind(&SocketAddr::from(([127, 0, 0, 1], 3000)))
|
||||
/// .serve(app.into_make_service())
|
||||
/// .await?;
|
||||
/// # Ok::<_, hyper::Error>(())
|
||||
/// # };
|
||||
/// ```
|
||||
///
|
||||
/// [`MakeService`]: tower::make::MakeService
|
||||
pub fn into_make_service(self) -> IntoMakeService<IntoService<H, T, S, B>> {
|
||||
IntoMakeService::new(self.service)
|
||||
}
|
||||
|
||||
/// Convert the handler into a [`MakeService`] which stores information
|
||||
/// about the incoming connection.
|
||||
///
|
||||
/// See [`Router::into_make_service_with_connect_info`] for more details.
|
||||
///
|
||||
/// ```rust
|
||||
/// use axum::{
|
||||
/// Server,
|
||||
/// handler::Handler,
|
||||
/// response::IntoResponse,
|
||||
/// extract::{ConnectInfo, State},
|
||||
/// };
|
||||
/// use std::net::SocketAddr;
|
||||
///
|
||||
/// #[derive(Clone)]
|
||||
/// struct AppState {};
|
||||
///
|
||||
/// async fn handler(
|
||||
/// ConnectInfo(addr): ConnectInfo<SocketAddr>,
|
||||
/// State(state): State<AppState>,
|
||||
/// ) -> String {
|
||||
/// format!("Hello {}", addr)
|
||||
/// }
|
||||
///
|
||||
/// let app = handler.with_state(AppState {});
|
||||
///
|
||||
/// # async {
|
||||
/// Server::bind(&SocketAddr::from(([127, 0, 0, 1], 3000)))
|
||||
/// .serve(app.into_make_service_with_connect_info::<SocketAddr>())
|
||||
/// .await?;
|
||||
/// # Ok::<_, hyper::Error>(())
|
||||
/// # };
|
||||
/// ```
|
||||
///
|
||||
/// [`MakeService`]: tower::make::MakeService
|
||||
/// [`Router::into_make_service_with_connect_info`]: crate::routing::Router::into_make_service_with_connect_info
|
||||
pub fn into_make_service_with_connect_info<C>(
|
||||
self,
|
||||
) -> IntoMakeServiceWithConnectInfo<IntoService<H, T, S, B>, C> {
|
||||
IntoMakeServiceWithConnectInfo::new(self.service)
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, T, S, B> Service<Request<B>> for WithState<H, T, S, B>
|
||||
where
|
||||
H: Handler<T, S, B> + Clone + Send + 'static,
|
||||
B: Send + 'static,
|
||||
S: Clone,
|
||||
{
|
||||
type Response = <IntoService<H, T, S, B> as Service<Request<B>>>::Response;
|
||||
type Error = <IntoService<H, T, S, B> as Service<Request<B>>>::Error;
|
||||
type Future = <IntoService<H, T, S, B> as Service<Request<B>>>::Future;
|
||||
|
||||
#[inline]
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.service.poll_ready(cx)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn call(&mut self, req: Request<B>) -> Self::Future {
|
||||
self.service.call(req)
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, T, S, B> std::fmt::Debug for WithState<H, T, S, B> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("WithState")
|
||||
.field("service", &self.service)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, T, S, B> Clone for WithState<H, T, S, B>
|
||||
where
|
||||
H: Clone,
|
||||
S: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
service: self.service.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -94,16 +94,17 @@ use std::ops::{Deref, DerefMut};
|
||||
pub struct Json<T>(pub T);
|
||||
|
||||
#[async_trait]
|
||||
impl<T, B> FromRequest<B> for Json<T>
|
||||
impl<T, S, B> FromRequest<S, B> for Json<T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
B: HttpBody + Send,
|
||||
B::Data: Send,
|
||||
B::Error: Into<BoxError>,
|
||||
S: Send,
|
||||
{
|
||||
type Rejection = JsonRejection;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
if json_content_type(req) {
|
||||
let bytes = Bytes::from_request(req).await?;
|
||||
|
||||
@@ -136,7 +137,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn json_content_type<B>(req: &RequestParts<B>) -> bool {
|
||||
fn json_content_type<S, B>(req: &RequestParts<S, B>) -> bool {
|
||||
let content_type = if let Some(content_type) = req.headers().get(header::CONTENT_TYPE) {
|
||||
content_type
|
||||
} else {
|
||||
|
||||
+46
-10
@@ -168,13 +168,48 @@
|
||||
//! pool of database connections or clients to other services.
|
||||
//!
|
||||
//! The two most common ways of doing that are:
|
||||
//! - Using the [`State`] extractor.
|
||||
//! - Using request extensions
|
||||
//! - Using closure captures
|
||||
//!
|
||||
//! ## Using the [`State`] extractor
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! use axum::{
|
||||
//! extract::State,
|
||||
//! routing::get,
|
||||
//! Router,
|
||||
//! };
|
||||
//! use std::sync::Arc;
|
||||
//!
|
||||
//! struct AppState {
|
||||
//! // ...
|
||||
//! }
|
||||
//!
|
||||
//! let shared_state = Arc::new(AppState { /* ... */ });
|
||||
//!
|
||||
//! let app = Router::with_state(shared_state)
|
||||
//! .route("/", get(handler));
|
||||
//!
|
||||
//! async fn handler(
|
||||
//! State(state): State<Arc<AppState>>,
|
||||
//! ) {
|
||||
//! // ...
|
||||
//! }
|
||||
//! # async {
|
||||
//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
//! # };
|
||||
//! ```
|
||||
//!
|
||||
//! You should prefer using [`State`] if possible since it's more type safe. The downside is that
|
||||
//! its less dynamic than request extensions.
|
||||
//!
|
||||
//! See [`State`] for more details about accessing state.
|
||||
//!
|
||||
//! ## Using request extensions
|
||||
//!
|
||||
//! The easiest way to extract state in handlers is using [`Extension`](crate::extract::Extension)
|
||||
//! as layer and extractor:
|
||||
//! Another way to extract state in handlers is using [`Extension`](crate::extract::Extension) as
|
||||
//! layer and extractor:
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! use axum::{
|
||||
@@ -184,18 +219,18 @@
|
||||
//! };
|
||||
//! use std::sync::Arc;
|
||||
//!
|
||||
//! struct State {
|
||||
//! struct AppState {
|
||||
//! // ...
|
||||
//! }
|
||||
//!
|
||||
//! let shared_state = Arc::new(State { /* ... */ });
|
||||
//! let shared_state = Arc::new(AppState { /* ... */ });
|
||||
//!
|
||||
//! let app = Router::new()
|
||||
//! .route("/", get(handler))
|
||||
//! .layer(Extension(shared_state));
|
||||
//!
|
||||
//! async fn handler(
|
||||
//! Extension(state): Extension<Arc<State>>,
|
||||
//! Extension(state): Extension<Arc<AppState>>,
|
||||
//! ) {
|
||||
//! // ...
|
||||
//! }
|
||||
@@ -223,11 +258,11 @@
|
||||
//! use std::sync::Arc;
|
||||
//! use serde::Deserialize;
|
||||
//!
|
||||
//! struct State {
|
||||
//! struct AppState {
|
||||
//! // ...
|
||||
//! }
|
||||
//!
|
||||
//! let shared_state = Arc::new(State { /* ... */ });
|
||||
//! let shared_state = Arc::new(AppState { /* ... */ });
|
||||
//!
|
||||
//! let app = Router::new()
|
||||
//! .route(
|
||||
@@ -245,11 +280,11 @@
|
||||
//! }),
|
||||
//! );
|
||||
//!
|
||||
//! async fn get_user(Path(user_id): Path<String>, state: Arc<State>) {
|
||||
//! async fn get_user(Path(user_id): Path<String>, state: Arc<AppState>) {
|
||||
//! // ...
|
||||
//! }
|
||||
//!
|
||||
//! async fn create_user(Json(payload): Json<CreateUserPayload>, state: Arc<State>) {
|
||||
//! async fn create_user(Json(payload): Json<CreateUserPayload>, state: Arc<AppState>) {
|
||||
//! // ...
|
||||
//! }
|
||||
//!
|
||||
@@ -263,7 +298,7 @@
|
||||
//! ```
|
||||
//!
|
||||
//! The downside to this approach is that it's a little more verbose than using
|
||||
//! extensions.
|
||||
//! [`State`] or extensions.
|
||||
//!
|
||||
//! # Building integrations for axum
|
||||
//!
|
||||
@@ -350,6 +385,7 @@
|
||||
//! [`Infallible`]: std::convert::Infallible
|
||||
//! [load shed]: tower::load_shed
|
||||
//! [`axum-core`]: http://crates.io/crates/axum-core
|
||||
//! [`State`]: crate::extract::State
|
||||
|
||||
#![warn(
|
||||
clippy::all,
|
||||
|
||||
@@ -45,13 +45,14 @@ use tower_service::Service;
|
||||
/// struct RequireAuth;
|
||||
///
|
||||
/// #[async_trait]
|
||||
/// impl<B> FromRequest<B> for RequireAuth
|
||||
/// impl<S, B> FromRequest<S, B> for RequireAuth
|
||||
/// where
|
||||
/// B: Send,
|
||||
/// S: Send,
|
||||
/// {
|
||||
/// type Rejection = StatusCode;
|
||||
///
|
||||
/// async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
/// async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
/// let auth_header = req
|
||||
/// .headers()
|
||||
/// .get(header::AUTHORIZATION)
|
||||
@@ -166,23 +167,23 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, E, ReqBody> Service<Request<ReqBody>> for FromExtractor<S, E>
|
||||
impl<S, E, B> Service<Request<B>> for FromExtractor<S, E>
|
||||
where
|
||||
E: FromRequest<ReqBody> + 'static,
|
||||
ReqBody: Default + Send + 'static,
|
||||
S: Service<Request<ReqBody>> + Clone,
|
||||
E: FromRequest<(), B> + 'static,
|
||||
B: Default + Send + 'static,
|
||||
S: Service<Request<B>> + Clone,
|
||||
S::Response: IntoResponse,
|
||||
{
|
||||
type Response = Response;
|
||||
type Error = S::Error;
|
||||
type Future = ResponseFuture<ReqBody, S, E>;
|
||||
type Future = ResponseFuture<B, S, E>;
|
||||
|
||||
#[inline]
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.inner.poll_ready(cx)
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request<ReqBody>) -> Self::Future {
|
||||
fn call(&mut self, req: Request<B>) -> Self::Future {
|
||||
let extract_future = Box::pin(async move {
|
||||
let mut req = RequestParts::new(req);
|
||||
let extracted = E::from_request(&mut req).await;
|
||||
@@ -201,35 +202,37 @@ where
|
||||
pin_project! {
|
||||
/// Response future for [`FromExtractor`].
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct ResponseFuture<ReqBody, S, E>
|
||||
pub struct ResponseFuture<B, S, E>
|
||||
where
|
||||
E: FromRequest<ReqBody>,
|
||||
S: Service<Request<ReqBody>>,
|
||||
E: FromRequest<(), B>,
|
||||
S: Service<Request<B>>,
|
||||
{
|
||||
#[pin]
|
||||
state: State<ReqBody, S, E>,
|
||||
state: State<B, S, E>,
|
||||
svc: Option<S>,
|
||||
}
|
||||
}
|
||||
|
||||
pin_project! {
|
||||
#[project = StateProj]
|
||||
enum State<ReqBody, S, E>
|
||||
enum State<B, S, E>
|
||||
where
|
||||
E: FromRequest<ReqBody>,
|
||||
S: Service<Request<ReqBody>>,
|
||||
E: FromRequest<(), B>,
|
||||
S: Service<Request<B>>,
|
||||
{
|
||||
Extracting { future: BoxFuture<'static, (RequestParts<ReqBody>, Result<E, E::Rejection>)> },
|
||||
Extracting {
|
||||
future: BoxFuture<'static, (RequestParts<(), B>, Result<E, E::Rejection>)>,
|
||||
},
|
||||
Call { #[pin] future: S::Future },
|
||||
}
|
||||
}
|
||||
|
||||
impl<ReqBody, S, E> Future for ResponseFuture<ReqBody, S, E>
|
||||
impl<B, S, E> Future for ResponseFuture<B, S, E>
|
||||
where
|
||||
E: FromRequest<ReqBody>,
|
||||
S: Service<Request<ReqBody>>,
|
||||
E: FromRequest<(), B>,
|
||||
S: Service<Request<B>>,
|
||||
S::Response: IntoResponse,
|
||||
ReqBody: Default,
|
||||
B: Default,
|
||||
{
|
||||
type Output = Result<Response, S::Error>;
|
||||
|
||||
@@ -277,13 +280,14 @@ mod tests {
|
||||
struct RequireAuth;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<B> FromRequest<B> for RequireAuth
|
||||
impl<S, B> FromRequest<S, B> for RequireAuth
|
||||
where
|
||||
B: Send,
|
||||
S: Send,
|
||||
{
|
||||
type Rejection = StatusCode;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
if let Some(auth) = req
|
||||
.headers()
|
||||
.get(header::AUTHORIZATION)
|
||||
|
||||
@@ -251,19 +251,19 @@ where
|
||||
macro_rules! impl_service {
|
||||
( $($ty:ident),* $(,)? ) => {
|
||||
#[allow(non_snake_case)]
|
||||
impl<F, Fut, Out, S, ReqBody, $($ty,)*> Service<Request<ReqBody>> for FromFn<F, S, ($($ty,)*)>
|
||||
impl<F, Fut, Out, S, B, $($ty,)*> Service<Request<B>> for FromFn<F, S, ($($ty,)*)>
|
||||
where
|
||||
F: FnMut($($ty),*, Next<ReqBody>) -> Fut + Clone + Send + 'static,
|
||||
$( $ty: FromRequest<ReqBody> + Send, )*
|
||||
F: FnMut($($ty),*, Next<B>) -> Fut + Clone + Send + 'static,
|
||||
$( $ty: FromRequest<(), B> + Send, )*
|
||||
Fut: Future<Output = Out> + Send + 'static,
|
||||
Out: IntoResponse + 'static,
|
||||
S: Service<Request<ReqBody>, Error = Infallible>
|
||||
S: Service<Request<B>, Error = Infallible>
|
||||
+ Clone
|
||||
+ Send
|
||||
+ 'static,
|
||||
S::Response: IntoResponse,
|
||||
S::Future: Send + 'static,
|
||||
ReqBody: Send + 'static,
|
||||
B: Send + 'static,
|
||||
{
|
||||
type Response = Response;
|
||||
type Error = Infallible;
|
||||
@@ -273,7 +273,7 @@ macro_rules! impl_service {
|
||||
self.inner.poll_ready(cx)
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request<ReqBody>) -> Self::Future {
|
||||
fn call(&mut self, req: Request<B>) -> Self::Future {
|
||||
let not_ready_inner = self.inner.clone();
|
||||
let ready_inner = std::mem::replace(&mut self.inner, not_ready_inner);
|
||||
|
||||
@@ -320,13 +320,13 @@ where
|
||||
}
|
||||
|
||||
/// The remainder of a middleware stack, including the handler.
|
||||
pub struct Next<ReqBody> {
|
||||
inner: BoxCloneService<Request<ReqBody>, Response, Infallible>,
|
||||
pub struct Next<B> {
|
||||
inner: BoxCloneService<Request<B>, Response, Infallible>,
|
||||
}
|
||||
|
||||
impl<ReqBody> Next<ReqBody> {
|
||||
impl<B> Next<B> {
|
||||
/// Execute the remaining middleware stack.
|
||||
pub async fn run(mut self, req: Request<ReqBody>) -> Response {
|
||||
pub async fn run(mut self, req: Request<B>) -> Response {
|
||||
match self.inner.call(req).await {
|
||||
Ok(res) => res,
|
||||
Err(err) => match err {},
|
||||
@@ -334,7 +334,7 @@ impl<ReqBody> Next<ReqBody> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<ReqBody> fmt::Debug for Next<ReqBody> {
|
||||
impl<B> fmt::Debug for Next<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("FromFnLayer")
|
||||
.field("inner", &self.inner)
|
||||
|
||||
@@ -93,7 +93,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
Router::<Body>::new()
|
||||
Router::<_, Body>::new()
|
||||
.route("/", get(impl_trait_ok))
|
||||
.route("/", get(impl_trait_err))
|
||||
.route("/", get(impl_trait_both))
|
||||
@@ -203,7 +203,7 @@ mod tests {
|
||||
)
|
||||
}
|
||||
|
||||
Router::<Body>::new()
|
||||
Router::<_, Body>::new()
|
||||
.route("/", get(status))
|
||||
.route("/", get(status_headermap))
|
||||
.route("/", get(status_header_array))
|
||||
|
||||
+337
-114
@@ -1,9 +1,11 @@
|
||||
//! Route to services and handlers based on HTTP methods.
|
||||
|
||||
use super::IntoMakeService;
|
||||
use crate::{
|
||||
body::{boxed, Body, Bytes, Empty, HttpBody},
|
||||
body::{Body, Bytes, HttpBody},
|
||||
error_handling::{HandleError, HandleErrorLayer},
|
||||
extract::connect_info::IntoMakeServiceWithConnectInfo,
|
||||
handler::Handler,
|
||||
handler::{Handler, IntoServiceStateInExtension},
|
||||
http::{Method, Request, StatusCode},
|
||||
response::Response,
|
||||
routing::{future::RouteFuture, Fallback, MethodFilter, Route},
|
||||
@@ -13,6 +15,7 @@ use bytes::BytesMut;
|
||||
use std::{
|
||||
convert::Infallible,
|
||||
fmt,
|
||||
marker::PhantomData,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use tower::{service_fn, util::MapResponseLayer};
|
||||
@@ -74,11 +77,12 @@ macro_rules! top_level_service_fn {
|
||||
$name:ident, $method:ident
|
||||
) => {
|
||||
$(#[$m])+
|
||||
pub fn $name<S, ReqBody>(svc: S) -> MethodRouter<ReqBody, S::Error>
|
||||
pub fn $name<T, S, B>(svc: T) -> MethodRouter<S, B, T::Error>
|
||||
where
|
||||
S: Service<Request<ReqBody>> + Clone + Send + 'static,
|
||||
S::Response: IntoResponse + 'static,
|
||||
S::Future: Send + 'static,
|
||||
T: Service<Request<B>> + Clone + Send + 'static,
|
||||
T::Response: IntoResponse + 'static,
|
||||
T::Future: Send + 'static,
|
||||
B: Send + 'static,
|
||||
{
|
||||
on_service(MethodFilter::$method, svc)
|
||||
}
|
||||
@@ -134,11 +138,12 @@ macro_rules! top_level_handler_fn {
|
||||
$name:ident, $method:ident
|
||||
) => {
|
||||
$(#[$m])+
|
||||
pub fn $name<H, T, B>(handler: H) -> MethodRouter<B, Infallible>
|
||||
pub fn $name<H, T, S, B>(handler: H) -> MethodRouter<S, B, Infallible>
|
||||
where
|
||||
H: Handler<T, B>,
|
||||
H: Handler<T, S, B>,
|
||||
B: Send + 'static,
|
||||
T: 'static,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
on(MethodFilter::$method, handler)
|
||||
}
|
||||
@@ -206,14 +211,14 @@ macro_rules! chained_service_fn {
|
||||
) => {
|
||||
$(#[$m])+
|
||||
#[track_caller]
|
||||
pub fn $name<S>(self, svc: S) -> Self
|
||||
pub fn $name<T>(self, svc: T) -> Self
|
||||
where
|
||||
S: Service<Request<ReqBody>, Error = E>
|
||||
T: Service<Request<B>, Error = E>
|
||||
+ Clone
|
||||
+ Send
|
||||
+ 'static,
|
||||
S::Response: IntoResponse + 'static,
|
||||
S::Future: Send + 'static,
|
||||
T::Response: IntoResponse + 'static,
|
||||
T::Future: Send + 'static,
|
||||
{
|
||||
self.on_service(MethodFilter::$method, svc)
|
||||
}
|
||||
@@ -272,8 +277,9 @@ macro_rules! chained_handler_fn {
|
||||
#[track_caller]
|
||||
pub fn $name<H, T>(self, handler: H) -> Self
|
||||
where
|
||||
H: Handler<T, B>,
|
||||
H: Handler<T, S, B>,
|
||||
T: 'static,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
self.on(MethodFilter::$method, handler)
|
||||
}
|
||||
@@ -314,11 +320,12 @@ top_level_service_fn!(trace_service, TRACE);
|
||||
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
/// # };
|
||||
/// ```
|
||||
pub fn on_service<S, ReqBody>(filter: MethodFilter, svc: S) -> MethodRouter<ReqBody, S::Error>
|
||||
pub fn on_service<T, S, B>(filter: MethodFilter, svc: T) -> MethodRouter<S, B, T::Error>
|
||||
where
|
||||
S: Service<Request<ReqBody>> + Clone + Send + 'static,
|
||||
S::Response: IntoResponse + 'static,
|
||||
S::Future: Send + 'static,
|
||||
T: Service<Request<B>> + Clone + Send + 'static,
|
||||
T::Response: IntoResponse + 'static,
|
||||
T::Future: Send + 'static,
|
||||
B: Send + 'static,
|
||||
{
|
||||
MethodRouter::new().on_service(filter, svc)
|
||||
}
|
||||
@@ -376,13 +383,16 @@ where
|
||||
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
/// # };
|
||||
/// ```
|
||||
pub fn any_service<S, ReqBody>(svc: S) -> MethodRouter<ReqBody, S::Error>
|
||||
pub fn any_service<T, S, B>(svc: T) -> MethodRouter<S, B, T::Error>
|
||||
where
|
||||
S: Service<Request<ReqBody>> + Clone + Send + 'static,
|
||||
S::Response: IntoResponse + 'static,
|
||||
S::Future: Send + 'static,
|
||||
T: Service<Request<B>> + Clone + Send + 'static,
|
||||
T::Response: IntoResponse + 'static,
|
||||
T::Future: Send + 'static,
|
||||
B: Send + 'static,
|
||||
{
|
||||
MethodRouter::new().fallback(svc).skip_allow_header()
|
||||
MethodRouter::new()
|
||||
.fallback_service(svc)
|
||||
.skip_allow_header()
|
||||
}
|
||||
|
||||
top_level_handler_fn!(delete, DELETE);
|
||||
@@ -413,11 +423,12 @@ top_level_handler_fn!(trace, TRACE);
|
||||
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
/// # };
|
||||
/// ```
|
||||
pub fn on<H, T, B>(filter: MethodFilter, handler: H) -> MethodRouter<B, Infallible>
|
||||
pub fn on<H, T, S, B>(filter: MethodFilter, handler: H) -> MethodRouter<S, B, Infallible>
|
||||
where
|
||||
H: Handler<T, B>,
|
||||
H: Handler<T, S, B>,
|
||||
B: Send + 'static,
|
||||
T: 'static,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
MethodRouter::new().on(filter, handler)
|
||||
}
|
||||
@@ -459,20 +470,48 @@ where
|
||||
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
/// # };
|
||||
/// ```
|
||||
pub fn any<H, T, B>(handler: H) -> MethodRouter<B, Infallible>
|
||||
pub fn any<H, T, S, B>(handler: H) -> MethodRouter<S, B, Infallible>
|
||||
where
|
||||
H: Handler<T, B>,
|
||||
H: Handler<T, S, B>,
|
||||
B: Send + 'static,
|
||||
T: 'static,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
MethodRouter::new()
|
||||
.fallback_boxed_response_body(handler.into_service())
|
||||
.fallback_boxed_response_body(IntoServiceStateInExtension::new(handler))
|
||||
.skip_allow_header()
|
||||
}
|
||||
|
||||
/// A [`Service`] that accepts requests based on a [`MethodFilter`] and
|
||||
/// allows chaining additional handlers and services.
|
||||
pub struct MethodRouter<B = Body, E = Infallible> {
|
||||
///
|
||||
/// # When does `MethodRouter` implement [`Service`]?
|
||||
///
|
||||
/// Whether or not `MethodRouter` implements [`Service`] depends on the state type it requires.
|
||||
///
|
||||
/// ```
|
||||
/// use tower::Service;
|
||||
/// use axum::{routing::get, extract::State, body::Body, http::Request};
|
||||
///
|
||||
/// // this `MethodRouter` doesn't require any state, i.e. the state is `()`,
|
||||
/// let method_router = get(|| async {});
|
||||
/// // and thus it implements `Service`
|
||||
/// assert_service(method_router);
|
||||
///
|
||||
/// // this requires a `String` and doesn't implement `Service`
|
||||
/// let method_router = get(|_: State<String>| async {});
|
||||
/// // until you provide the `String` with `.with_state(...)`
|
||||
/// let method_router_with_state = method_router.with_state(String::new());
|
||||
/// // and then it implements `Service`
|
||||
/// assert_service(method_router_with_state);
|
||||
///
|
||||
/// // helper to check that a value implements `Service`
|
||||
/// fn assert_service<S>(service: S)
|
||||
/// where
|
||||
/// S: Service<Request<Body>>,
|
||||
/// {}
|
||||
/// ```
|
||||
pub struct MethodRouter<S = (), B = Body, E = Infallible> {
|
||||
get: Option<Route<B, E>>,
|
||||
head: Option<Route<B, E>>,
|
||||
delete: Option<Route<B, E>>,
|
||||
@@ -483,6 +522,7 @@ pub struct MethodRouter<B = Body, E = Infallible> {
|
||||
trace: Option<Route<B, E>>,
|
||||
fallback: Fallback<B, E>,
|
||||
allow_header: AllowHeader,
|
||||
_marker: PhantomData<fn() -> S>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -511,7 +551,7 @@ impl AllowHeader {
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E> fmt::Debug for MethodRouter<B, E> {
|
||||
impl<S, B, E> fmt::Debug for MethodRouter<S, B, E> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("MethodRouter")
|
||||
.field("get", &self.get)
|
||||
@@ -527,32 +567,7 @@ impl<B, E> fmt::Debug for MethodRouter<B, E> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E> MethodRouter<B, E> {
|
||||
/// 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 mut response = Response::new(boxed(Empty::new()));
|
||||
*response.status_mut() = StatusCode::METHOD_NOT_ALLOWED;
|
||||
Ok(response)
|
||||
}));
|
||||
|
||||
Self {
|
||||
get: None,
|
||||
head: None,
|
||||
delete: None,
|
||||
options: None,
|
||||
patch: None,
|
||||
post: None,
|
||||
put: None,
|
||||
trace: None,
|
||||
allow_header: AllowHeader::None,
|
||||
fallback: Fallback::Default(fallback),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> MethodRouter<B, Infallible>
|
||||
impl<S, B> MethodRouter<S, B, Infallible>
|
||||
where
|
||||
B: Send + 'static,
|
||||
{
|
||||
@@ -582,10 +597,11 @@ where
|
||||
#[track_caller]
|
||||
pub fn on<H, T>(self, filter: MethodFilter, handler: H) -> Self
|
||||
where
|
||||
H: Handler<T, B>,
|
||||
H: Handler<T, S, B>,
|
||||
T: 'static,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
self.on_service_boxed_response_body(filter, handler.into_service())
|
||||
self.on_service_boxed_response_body(filter, IntoServiceStateInExtension::new(handler))
|
||||
}
|
||||
|
||||
chained_handler_fn!(delete, DELETE);
|
||||
@@ -597,6 +613,21 @@ where
|
||||
chained_handler_fn!(put, PUT);
|
||||
chained_handler_fn!(trace, TRACE);
|
||||
|
||||
/// Add a fallback [`Handler`] to the router.
|
||||
pub fn fallback<H, T>(self, handler: H) -> Self
|
||||
where
|
||||
H: Handler<T, S, B>,
|
||||
T: 'static,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
self.fallback_service(IntoServiceStateInExtension::new(handler))
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> MethodRouter<(), B, Infallible>
|
||||
where
|
||||
B: Send + 'static,
|
||||
{
|
||||
/// Convert the handler into a [`MakeService`].
|
||||
///
|
||||
/// This allows you to serve a single handler if you don't need any routing:
|
||||
@@ -666,7 +697,58 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<ReqBody, E> MethodRouter<ReqBody, E> {
|
||||
impl<S, B, E> MethodRouter<S, B, E>
|
||||
where
|
||||
B: Send + 'static,
|
||||
{
|
||||
/// 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 {
|
||||
Ok(StatusCode::METHOD_NOT_ALLOWED.into_response())
|
||||
}));
|
||||
|
||||
Self {
|
||||
get: None,
|
||||
head: None,
|
||||
delete: None,
|
||||
options: None,
|
||||
patch: None,
|
||||
post: None,
|
||||
put: None,
|
||||
trace: None,
|
||||
allow_header: AllowHeader::None,
|
||||
fallback: Fallback::Default(fallback),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Provide the state.
|
||||
///
|
||||
/// See [`State`](crate::extract::State) for more details about accessing state.
|
||||
pub fn with_state(self, state: S) -> WithState<S, B, E> {
|
||||
WithState {
|
||||
method_router: self,
|
||||
state,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn downcast_state<S2>(self) -> MethodRouter<S2, B, E> {
|
||||
MethodRouter {
|
||||
get: self.get,
|
||||
head: self.head,
|
||||
delete: self.delete,
|
||||
options: self.options,
|
||||
patch: self.patch,
|
||||
post: self.post,
|
||||
put: self.put,
|
||||
trace: self.trace,
|
||||
fallback: self.fallback,
|
||||
allow_header: self.allow_header,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Chain an additional service that will accept requests matching the given
|
||||
/// `MethodFilter`.
|
||||
///
|
||||
@@ -693,11 +775,11 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
|
||||
/// # };
|
||||
/// ```
|
||||
#[track_caller]
|
||||
pub fn on_service<S>(self, filter: MethodFilter, svc: S) -> Self
|
||||
pub fn on_service<T>(self, filter: MethodFilter, svc: T) -> Self
|
||||
where
|
||||
S: Service<Request<ReqBody>, Error = E> + Clone + Send + 'static,
|
||||
S::Response: IntoResponse + 'static,
|
||||
S::Future: Send + 'static,
|
||||
T: Service<Request<B>, Error = E> + Clone + Send + 'static,
|
||||
T::Response: IntoResponse + 'static,
|
||||
T::Future: Send + 'static,
|
||||
{
|
||||
self.on_service_boxed_response_body(filter, svc)
|
||||
}
|
||||
@@ -712,30 +794,30 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
|
||||
chained_service_fn!(trace_service, TRACE);
|
||||
|
||||
#[doc = include_str!("../docs/method_routing/fallback.md")]
|
||||
pub fn fallback<S>(mut self, svc: S) -> Self
|
||||
pub fn fallback_service<T>(mut self, svc: T) -> Self
|
||||
where
|
||||
S: Service<Request<ReqBody>, Error = E> + Clone + Send + 'static,
|
||||
S::Response: IntoResponse + 'static,
|
||||
S::Future: Send + 'static,
|
||||
T: Service<Request<B>, Error = E> + Clone + Send + 'static,
|
||||
T::Response: IntoResponse + 'static,
|
||||
T::Future: Send + 'static,
|
||||
{
|
||||
self.fallback = Fallback::Custom(Route::new(svc));
|
||||
self
|
||||
}
|
||||
|
||||
fn fallback_boxed_response_body<S>(mut self, svc: S) -> Self
|
||||
fn fallback_boxed_response_body<T>(mut self, svc: T) -> Self
|
||||
where
|
||||
S: Service<Request<ReqBody>, Error = E> + Clone + Send + 'static,
|
||||
S::Response: IntoResponse + 'static,
|
||||
S::Future: Send + 'static,
|
||||
T: Service<Request<B>, Error = E> + Clone + Send + 'static,
|
||||
T::Response: IntoResponse + 'static,
|
||||
T::Future: Send + 'static,
|
||||
{
|
||||
self.fallback = Fallback::Custom(Route::new(svc));
|
||||
self
|
||||
}
|
||||
|
||||
#[doc = include_str!("../docs/method_routing/layer.md")]
|
||||
pub fn layer<L, NewReqBody, NewError>(self, layer: L) -> MethodRouter<NewReqBody, NewError>
|
||||
pub fn layer<L, NewReqBody, NewError>(self, layer: L) -> MethodRouter<S, NewReqBody, NewError>
|
||||
where
|
||||
L: Layer<Route<ReqBody, E>>,
|
||||
L: Layer<Route<B, E>>,
|
||||
L::Service: Service<Request<NewReqBody>, Error = NewError> + Clone + Send + 'static,
|
||||
<L::Service as Service<Request<NewReqBody>>>::Response: IntoResponse + 'static,
|
||||
<L::Service as Service<Request<NewReqBody>>>::Future: Send + 'static,
|
||||
@@ -757,16 +839,17 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
|
||||
trace: self.trace.map(layer_fn),
|
||||
fallback: self.fallback.map(layer_fn),
|
||||
allow_header: self.allow_header,
|
||||
_marker: self._marker,
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = include_str!("../docs/method_routing/route_layer.md")]
|
||||
pub fn route_layer<L>(mut self, layer: L) -> MethodRouter<ReqBody, E>
|
||||
pub fn route_layer<L>(mut self, layer: L) -> MethodRouter<S, B, E>
|
||||
where
|
||||
L: Layer<Route<ReqBody, E>>,
|
||||
L::Service: Service<Request<ReqBody>, Error = E> + Clone + Send + 'static,
|
||||
<L::Service as Service<Request<ReqBody>>>::Response: IntoResponse + 'static,
|
||||
<L::Service as Service<Request<ReqBody>>>::Future: Send + 'static,
|
||||
L: Layer<Route<B, E>>,
|
||||
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,
|
||||
{
|
||||
let layer_fn = |svc| {
|
||||
let svc = layer.layer(svc);
|
||||
@@ -788,7 +871,7 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
|
||||
|
||||
#[doc = include_str!("../docs/method_routing/merge.md")]
|
||||
#[track_caller]
|
||||
pub fn merge(mut self, other: MethodRouter<ReqBody, E>) -> Self {
|
||||
pub fn merge(mut self, other: MethodRouter<S, B, E>) -> Self {
|
||||
// written using inner functions to generate less IR
|
||||
#[track_caller]
|
||||
fn merge_inner<T>(name: &str, first: Option<T>, second: Option<T>) -> Option<T> {
|
||||
@@ -836,26 +919,25 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
|
||||
/// 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<ReqBody, Infallible>
|
||||
pub fn handle_error<F, T>(self, f: F) -> MethodRouter<S, B, Infallible>
|
||||
where
|
||||
F: Clone + Send + 'static,
|
||||
HandleError<Route<ReqBody, E>, F, T>: Service<Request<ReqBody>, Error = Infallible>,
|
||||
<HandleError<Route<ReqBody, E>, F, T> as Service<Request<ReqBody>>>::Future: Send,
|
||||
<HandleError<Route<ReqBody, E>, F, T> as Service<Request<ReqBody>>>::Response:
|
||||
IntoResponse + Send,
|
||||
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,
|
||||
T: 'static,
|
||||
E: 'static,
|
||||
ReqBody: 'static,
|
||||
B: 'static,
|
||||
{
|
||||
self.layer(HandleErrorLayer::new(f))
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn on_service_boxed_response_body<S>(mut self, filter: MethodFilter, svc: S) -> Self
|
||||
fn on_service_boxed_response_body<T>(mut self, filter: MethodFilter, svc: T) -> Self
|
||||
where
|
||||
S: Service<Request<ReqBody>, Error = E> + Clone + Send + 'static,
|
||||
S::Response: IntoResponse + 'static,
|
||||
S::Future: Send + 'static,
|
||||
T: Service<Request<B>, Error = E> + Clone + Send + 'static,
|
||||
T::Response: IntoResponse + 'static,
|
||||
T::Future: Send + 'static,
|
||||
{
|
||||
// written using an inner function to generate less IR
|
||||
fn set_service<T>(
|
||||
@@ -991,7 +1073,25 @@ fn append_allow_header(allow_header: &mut AllowHeader, method: &'static str) {
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E> Clone for MethodRouter<B, E> {
|
||||
impl<B, E> Service<Request<B>> for MethodRouter<(), B, E>
|
||||
where
|
||||
B: HttpBody + Send + 'static,
|
||||
{
|
||||
type Response = Response;
|
||||
type Error = E;
|
||||
type Future = RouteFuture<B, E>;
|
||||
|
||||
#[inline]
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request<B>) -> Self::Future {
|
||||
self.clone().with_state(()).call(req)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, B, E> Clone for MethodRouter<S, B, E> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
get: self.get.clone(),
|
||||
@@ -1004,11 +1104,12 @@ impl<B, E> Clone for MethodRouter<B, E> {
|
||||
trace: self.trace.clone(),
|
||||
fallback: self.fallback.clone(),
|
||||
allow_header: self.allow_header.clone(),
|
||||
_marker: self._marker,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E> Default for MethodRouter<B, E>
|
||||
impl<S, B, E> Default for MethodRouter<S, B, E>
|
||||
where
|
||||
B: Send + 'static,
|
||||
{
|
||||
@@ -1017,9 +1118,72 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E> Service<Request<B>> for MethodRouter<B, E>
|
||||
/// A [`MethodRouter`] which has access to some state.
|
||||
///
|
||||
/// Implements [`Service`].
|
||||
///
|
||||
/// The state can be extracted with [`State`](crate::extract::State).
|
||||
///
|
||||
/// Created with [`MethodRouter::with_state`]
|
||||
pub struct WithState<S, B, E> {
|
||||
method_router: MethodRouter<S, B, E>,
|
||||
state: S,
|
||||
}
|
||||
|
||||
impl<S, B, E> WithState<S, B, E> {
|
||||
/// Get a reference to the state.
|
||||
pub fn state(&self) -> &S {
|
||||
&self.state
|
||||
}
|
||||
|
||||
/// Convert the handler into a [`MakeService`].
|
||||
///
|
||||
/// See [`MethodRouter::into_make_service`] for more details.
|
||||
///
|
||||
/// [`MakeService`]: tower::make::MakeService
|
||||
pub fn into_make_service(self) -> IntoMakeService<Self> {
|
||||
IntoMakeService::new(self)
|
||||
}
|
||||
|
||||
/// Convert the router into a [`MakeService`] which stores information
|
||||
/// about the incoming connection.
|
||||
///
|
||||
/// See [`MethodRouter::into_make_service_with_connect_info`] for more details.
|
||||
///
|
||||
/// [`MakeService`]: tower::make::MakeService
|
||||
pub fn into_make_service_with_connect_info<C>(self) -> IntoMakeServiceWithConnectInfo<Self, C> {
|
||||
IntoMakeServiceWithConnectInfo::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, B, E> Clone for WithState<S, B, E>
|
||||
where
|
||||
S: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
method_router: self.method_router.clone(),
|
||||
state: self.state.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, B, E> fmt::Debug for WithState<S, B, E>
|
||||
where
|
||||
S: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("WithState")
|
||||
.field("method_router", &self.method_router)
|
||||
.field("state", &self.state)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, B, E> Service<Request<B>> for WithState<S, B, E>
|
||||
where
|
||||
B: HttpBody,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
type Response = Response;
|
||||
type Error = E;
|
||||
@@ -1030,7 +1194,7 @@ where
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request<B>) -> Self::Future {
|
||||
fn call(&mut self, mut req: Request<B>) -> Self::Future {
|
||||
macro_rules! call {
|
||||
(
|
||||
$req:expr,
|
||||
@@ -1051,18 +1215,25 @@ where
|
||||
|
||||
// written with a pattern match like this to ensure we call all routes
|
||||
let Self {
|
||||
get,
|
||||
head,
|
||||
delete,
|
||||
options,
|
||||
patch,
|
||||
post,
|
||||
put,
|
||||
trace,
|
||||
fallback,
|
||||
allow_header,
|
||||
state,
|
||||
method_router:
|
||||
MethodRouter {
|
||||
get,
|
||||
head,
|
||||
delete,
|
||||
options,
|
||||
patch,
|
||||
post,
|
||||
put,
|
||||
trace,
|
||||
fallback,
|
||||
allow_header,
|
||||
_marker: _,
|
||||
},
|
||||
} = self;
|
||||
|
||||
req.extensions_mut().insert(state.clone());
|
||||
|
||||
call!(req, method, HEAD, head);
|
||||
call!(req, method, HEAD, get);
|
||||
call!(req, method, GET, get);
|
||||
@@ -1091,7 +1262,7 @@ where
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{body::Body, error_handling::HandleErrorLayer};
|
||||
use crate::{body::Body, error_handling::HandleErrorLayer, extract::State};
|
||||
use axum_core::response::IntoResponse;
|
||||
use http::{header::ALLOW, HeaderMap};
|
||||
use std::time::Duration;
|
||||
@@ -1106,6 +1277,19 @@ mod tests {
|
||||
assert!(body.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_service_fn() {
|
||||
async fn handle(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
|
||||
Ok(Response::new(Body::from("ok")))
|
||||
}
|
||||
|
||||
let mut svc = get_service(service_fn(handle));
|
||||
|
||||
let (status, _, body) = call(Method::GET, &mut svc).await;
|
||||
assert_eq!(status, StatusCode::OK);
|
||||
assert_eq!(body, "ok");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_handler() {
|
||||
let mut svc = MethodRouter::new().get(ok);
|
||||
@@ -1183,7 +1367,7 @@ mod tests {
|
||||
delete_service(ServeDir::new("."))
|
||||
.handle_error(|_| async { StatusCode::NOT_FOUND }),
|
||||
)
|
||||
.fallback((|| async { StatusCode::NOT_FOUND }).into_service())
|
||||
.fallback(|| async { StatusCode::NOT_FOUND })
|
||||
.put(ok)
|
||||
.layer(
|
||||
ServiceBuilder::new()
|
||||
@@ -1243,9 +1427,9 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn allow_header_with_fallback() {
|
||||
let mut svc = MethodRouter::new().get(ok).fallback(
|
||||
(|| async { (StatusCode::METHOD_NOT_ALLOWED, "Method not allowed") }).into_service(),
|
||||
);
|
||||
let mut svc = MethodRouter::new()
|
||||
.get(ok)
|
||||
.fallback(|| async { (StatusCode::METHOD_NOT_ALLOWED, "Method not allowed") });
|
||||
|
||||
let (status, headers, _) = call(Method::DELETE, &mut svc).await;
|
||||
assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED);
|
||||
@@ -1267,9 +1451,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
let mut svc = MethodRouter::new()
|
||||
.get(ok)
|
||||
.fallback(fallback.into_service());
|
||||
let mut svc = MethodRouter::new().get(ok).fallback(fallback);
|
||||
|
||||
let (status, _, _) = call(Method::GET, &mut svc).await;
|
||||
assert_eq!(status, StatusCode::OK);
|
||||
@@ -1287,7 +1469,7 @@ mod tests {
|
||||
expected = "Overlapping method route. Cannot add two method routes that both handle `GET`"
|
||||
)]
|
||||
async fn handler_overlaps() {
|
||||
let _: MethodRouter = get(ok).get(ok);
|
||||
let _: MethodRouter<()> = get(ok).get(ok);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -1295,17 +1477,58 @@ mod tests {
|
||||
expected = "Overlapping method route. Cannot add two method routes that both handle `POST`"
|
||||
)]
|
||||
async fn service_overlaps() {
|
||||
let _: MethodRouter = post_service(ok.into_service()).post_service(ok.into_service());
|
||||
let _: MethodRouter<()> = post_service(IntoServiceStateInExtension::<_, _, (), _>::new(ok))
|
||||
.post_service(IntoServiceStateInExtension::<_, _, (), _>::new(ok));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_head_does_not_overlap() {
|
||||
let _: MethodRouter = get(ok).head(ok);
|
||||
let _: MethodRouter<()> = get(ok).head(ok);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn head_get_does_not_overlap() {
|
||||
let _: MethodRouter = head(ok).get(ok);
|
||||
let _: MethodRouter<()> = head(ok).get(ok);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn accessing_state() {
|
||||
let mut svc = MethodRouter::new()
|
||||
.get(|State(state): State<&'static str>| async move { state })
|
||||
.with_state("state");
|
||||
|
||||
let (status, _, text) = call(Method::GET, &mut svc).await;
|
||||
|
||||
assert_eq!(status, StatusCode::OK);
|
||||
assert_eq!(text, "state");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn fallback_accessing_state() {
|
||||
let mut svc = MethodRouter::new()
|
||||
.fallback(|State(state): State<&'static str>| async move { state })
|
||||
.with_state("state");
|
||||
|
||||
let (status, _, text) = call(Method::GET, &mut svc).await;
|
||||
|
||||
assert_eq!(status, StatusCode::OK);
|
||||
assert_eq!(text, "state");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn merge_accessing_state() {
|
||||
let one = get(|State(state): State<&'static str>| async move { state });
|
||||
let two = post(|State(state): State<&'static str>| async move { state });
|
||||
|
||||
let mut svc = one.merge(two).with_state("state");
|
||||
|
||||
let (status, _, text) = call(Method::GET, &mut svc).await;
|
||||
assert_eq!(status, StatusCode::OK);
|
||||
assert_eq!(text, "state");
|
||||
|
||||
let (status, _, _) = call(Method::POST, &mut svc).await;
|
||||
assert_eq!(status, StatusCode::OK);
|
||||
assert_eq!(text, "state");
|
||||
}
|
||||
|
||||
async fn call<S>(method: Method, svc: &mut S) -> (StatusCode, HeaderMap, String)
|
||||
|
||||
+130
-56
@@ -4,8 +4,10 @@ use self::{future::RouteFuture, not_found::NotFound};
|
||||
use crate::{
|
||||
body::{Body, HttpBody},
|
||||
extract::connect_info::IntoMakeServiceWithConnectInfo,
|
||||
handler::Handler,
|
||||
response::Response,
|
||||
util::try_downcast,
|
||||
Extension,
|
||||
};
|
||||
use axum_core::response::IntoResponse;
|
||||
use http::Request;
|
||||
@@ -22,10 +24,10 @@ use tower_layer::Layer;
|
||||
use tower_service::Service;
|
||||
|
||||
pub mod future;
|
||||
pub mod method_routing;
|
||||
|
||||
mod into_make_service;
|
||||
mod method_filter;
|
||||
mod method_routing;
|
||||
mod not_found;
|
||||
mod route;
|
||||
mod strip_prefix;
|
||||
@@ -59,15 +61,20 @@ impl RouteId {
|
||||
}
|
||||
|
||||
/// The router type for composing handlers and services.
|
||||
pub struct Router<B = Body> {
|
||||
routes: HashMap<RouteId, Endpoint<B>>,
|
||||
pub struct Router<S = (), B = Body> {
|
||||
state: S,
|
||||
routes: HashMap<RouteId, Endpoint<S, B>>,
|
||||
node: Arc<Node>,
|
||||
fallback: Fallback<B>,
|
||||
}
|
||||
|
||||
impl<B> Clone for Router<B> {
|
||||
impl<S, B> Clone for Router<S, B>
|
||||
where
|
||||
S: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
state: self.state.clone(),
|
||||
routes: self.routes.clone(),
|
||||
node: Arc::clone(&self.node),
|
||||
fallback: self.fallback.clone(),
|
||||
@@ -75,18 +82,23 @@ impl<B> Clone for Router<B> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> Default for Router<B>
|
||||
impl<S, B> Default for Router<S, B>
|
||||
where
|
||||
B: HttpBody + Send + 'static,
|
||||
S: Default + Clone + Send + Sync + 'static,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
Self::with_state(S::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> fmt::Debug for Router<B> {
|
||||
impl<S, B> fmt::Debug for Router<S, B>
|
||||
where
|
||||
S: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Router")
|
||||
.field("state", &self.state)
|
||||
.field("routes", &self.routes)
|
||||
.field("node", &self.node)
|
||||
.field("fallback", &self.fallback)
|
||||
@@ -97,7 +109,7 @@ impl<B> fmt::Debug for Router<B> {
|
||||
pub(crate) const NEST_TAIL_PARAM: &str = "__private__axum_nest_tail_param";
|
||||
const NEST_TAIL_PARAM_CAPTURE: &str = "/*__private__axum_nest_tail_param";
|
||||
|
||||
impl<B> Router<B>
|
||||
impl<B> Router<(), B>
|
||||
where
|
||||
B: HttpBody + Send + 'static,
|
||||
{
|
||||
@@ -106,7 +118,24 @@ where
|
||||
/// Unless you add additional routes this will respond with `404 Not Found` to
|
||||
/// all requests.
|
||||
pub fn new() -> Self {
|
||||
Self::with_state(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, B> Router<S, B>
|
||||
where
|
||||
B: HttpBody + Send + 'static,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
/// Create a new `Router` with the given state.
|
||||
///
|
||||
/// See [`State`](crate::extract::State) for more details about accessing state.
|
||||
///
|
||||
/// Unless you add additional routes this will respond with `404 Not Found` to
|
||||
/// all requests.
|
||||
pub fn with_state(state: S) -> Self {
|
||||
Self {
|
||||
state,
|
||||
routes: Default::default(),
|
||||
node: Default::default(),
|
||||
fallback: Fallback::Default(Route::new(NotFound)),
|
||||
@@ -115,12 +144,8 @@ where
|
||||
|
||||
#[doc = include_str!("../docs/routing/route.md")]
|
||||
#[track_caller]
|
||||
pub fn route<T>(mut self, path: &str, service: T) -> Self
|
||||
where
|
||||
T: Service<Request<B>, Error = Infallible> + Clone + Send + 'static,
|
||||
T::Response: IntoResponse,
|
||||
T::Future: Send + 'static,
|
||||
{
|
||||
pub fn route(mut self, path: &str, method_router: MethodRouter<S, B>) -> Self {
|
||||
#[track_caller]
|
||||
fn validate_path(path: &str) {
|
||||
if path.is_empty() {
|
||||
panic!("Paths must start with a `/`. Use \"/\" for root routes");
|
||||
@@ -131,39 +156,53 @@ where
|
||||
|
||||
validate_path(path);
|
||||
|
||||
let service = match try_downcast::<Router<B>, _>(service) {
|
||||
let id = RouteId::next();
|
||||
|
||||
let endpoint = if let Some((route_id, Endpoint::MethodRouter(prev_method_router))) = self
|
||||
.node
|
||||
.path_to_route_id
|
||||
.get(path)
|
||||
.and_then(|route_id| self.routes.get(route_id).map(|svc| (*route_id, svc)))
|
||||
{
|
||||
// if we're adding a new `MethodRouter` to a route that already has one just
|
||||
// merge them. This makes `.route("/", get(_)).route("/", post(_))` work
|
||||
let service = Endpoint::MethodRouter(prev_method_router.clone().merge(method_router));
|
||||
self.routes.insert(route_id, service);
|
||||
return self;
|
||||
} else {
|
||||
Endpoint::MethodRouter(method_router)
|
||||
};
|
||||
|
||||
self.set_node(path, id);
|
||||
self.routes.insert(id, endpoint);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
#[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::Response: IntoResponse,
|
||||
T::Future: Send + 'static,
|
||||
{
|
||||
if path.is_empty() {
|
||||
panic!("Paths must start with a `/`. Use \"/\" for root routes");
|
||||
} else if !path.starts_with('/') {
|
||||
panic!("Paths must start with a `/`");
|
||||
}
|
||||
|
||||
let service = match try_downcast::<Router<S, B>, _>(service) {
|
||||
Ok(_) => {
|
||||
panic!("Invalid route: `Router::route` cannot be used with `Router`s. Use `Router::nest` instead")
|
||||
panic!("Invalid route: `Router::route_service` cannot be used with `Router`s. Use `Router::nest` instead")
|
||||
}
|
||||
Err(svc) => svc,
|
||||
};
|
||||
|
||||
let id = RouteId::next();
|
||||
|
||||
let service = match try_downcast::<MethodRouter<B, Infallible>, _>(service) {
|
||||
Ok(method_router) => {
|
||||
if let Some((route_id, Endpoint::MethodRouter(prev_method_router))) = self
|
||||
.node
|
||||
.path_to_route_id
|
||||
.get(path)
|
||||
.and_then(|route_id| self.routes.get(route_id).map(|svc| (*route_id, svc)))
|
||||
{
|
||||
// if we're adding a new `MethodRouter` to a route that already has one just
|
||||
// merge them. This makes `.route("/", get(_)).route("/", post(_))` work
|
||||
let service =
|
||||
Endpoint::MethodRouter(prev_method_router.clone().merge(method_router));
|
||||
self.routes.insert(route_id, service);
|
||||
return self;
|
||||
} else {
|
||||
Endpoint::MethodRouter(method_router)
|
||||
}
|
||||
}
|
||||
Err(service) => Endpoint::Route(Route::new(service)),
|
||||
};
|
||||
|
||||
let endpoint = Endpoint::Route(Route::new(service));
|
||||
self.set_node(path, id);
|
||||
|
||||
self.routes.insert(id, service);
|
||||
self.routes.insert(id, endpoint);
|
||||
|
||||
self
|
||||
}
|
||||
@@ -204,15 +243,15 @@ where
|
||||
};
|
||||
|
||||
let svc = strip_prefix::StripPrefix::new(svc, prefix);
|
||||
self = self.route(&path, svc.clone());
|
||||
self = self.route_service(&path, svc.clone());
|
||||
|
||||
// `/*rest` is not matched by `/` so we need to also register a router at the
|
||||
// prefix itself. Otherwise if you were to nest at `/foo` then `/foo` itself
|
||||
// wouldn't match, which it should
|
||||
self = self.route(prefix, svc.clone());
|
||||
self = self.route_service(prefix, svc.clone());
|
||||
if !prefix.ends_with('/') {
|
||||
// same goes for `/foo/`, that should also match
|
||||
self = self.route(&format!("{prefix}/"), svc);
|
||||
self = self.route_service(&format!("{prefix}/"), svc);
|
||||
}
|
||||
|
||||
self
|
||||
@@ -220,11 +259,13 @@ where
|
||||
|
||||
#[doc = include_str!("../docs/routing/merge.md")]
|
||||
#[track_caller]
|
||||
pub fn merge<R>(mut self, other: R) -> Self
|
||||
pub fn merge<S2, R>(mut self, other: R) -> Self
|
||||
where
|
||||
R: Into<Router<B>>,
|
||||
R: Into<Router<S2, B>>,
|
||||
S2: Clone + Send + Sync + 'static,
|
||||
{
|
||||
let Router {
|
||||
state,
|
||||
routes,
|
||||
node,
|
||||
fallback,
|
||||
@@ -236,8 +277,15 @@ where
|
||||
.get(&id)
|
||||
.expect("no path for route id. This is a bug in axum. Please file an issue");
|
||||
self = match route {
|
||||
Endpoint::MethodRouter(route) => self.route(path, route),
|
||||
Endpoint::Route(route) => self.route(path, route),
|
||||
Endpoint::MethodRouter(method_router) => self.route(
|
||||
path,
|
||||
method_router
|
||||
// this will set the state for each route
|
||||
// such we don't override the inner state later in `MethodRouterWithState`
|
||||
.layer(Extension(state.clone()))
|
||||
.downcast_state(),
|
||||
),
|
||||
Endpoint::Route(route) => self.route_service(path, route),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -254,7 +302,7 @@ where
|
||||
}
|
||||
|
||||
#[doc = include_str!("../docs/routing/layer.md")]
|
||||
pub fn layer<L, NewReqBody>(self, layer: L) -> Router<NewReqBody>
|
||||
pub fn layer<L, NewReqBody>(self, layer: L) -> Router<S, NewReqBody>
|
||||
where
|
||||
L: Layer<Route<B>>,
|
||||
L::Service: Service<Request<NewReqBody>> + Clone + Send + 'static,
|
||||
@@ -285,6 +333,7 @@ where
|
||||
let fallback = self.fallback.map(|svc| Route::new(layer.layer(svc)));
|
||||
|
||||
Router {
|
||||
state: self.state,
|
||||
routes,
|
||||
node: self.node,
|
||||
fallback,
|
||||
@@ -321,6 +370,7 @@ where
|
||||
.collect();
|
||||
|
||||
Router {
|
||||
state: self.state,
|
||||
routes,
|
||||
node: self.node,
|
||||
fallback: self.fallback,
|
||||
@@ -328,7 +378,19 @@ where
|
||||
}
|
||||
|
||||
#[doc = include_str!("../docs/routing/fallback.md")]
|
||||
pub fn fallback<T>(mut self, svc: T) -> Self
|
||||
pub fn fallback<H, T>(self, handler: H) -> Self
|
||||
where
|
||||
H: Handler<T, S, B>,
|
||||
T: 'static,
|
||||
{
|
||||
let state = self.state.clone();
|
||||
self.fallback_service(handler.with_state(state))
|
||||
}
|
||||
|
||||
/// Add a fallback [`Service`] to the router.
|
||||
///
|
||||
/// See [`Router::fallback`] for more details.
|
||||
pub fn fallback_service<T>(mut self, svc: T) -> Self
|
||||
where
|
||||
T: Service<Request<B>, Error = Infallible> + Clone + Send + 'static,
|
||||
T::Response: IntoResponse,
|
||||
@@ -422,15 +484,21 @@ where
|
||||
.clone();
|
||||
|
||||
match &mut route {
|
||||
Endpoint::MethodRouter(inner) => inner.call(req),
|
||||
Endpoint::MethodRouter(inner) => inner.clone().with_state(self.state.clone()).call(req),
|
||||
Endpoint::Route(inner) => inner.call(req),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a reference to the state.
|
||||
pub fn state(&self) -> &S {
|
||||
&self.state
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> Service<Request<B>> for Router<B>
|
||||
impl<S, B> Service<Request<B>> for Router<S, B>
|
||||
where
|
||||
B: HttpBody + Send + 'static,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
type Response = Response;
|
||||
type Error = Infallible;
|
||||
@@ -545,12 +613,15 @@ impl<B, E> Fallback<B, E> {
|
||||
}
|
||||
}
|
||||
|
||||
enum Endpoint<B> {
|
||||
MethodRouter(MethodRouter<B>),
|
||||
enum Endpoint<S, B> {
|
||||
MethodRouter(MethodRouter<S, B, Infallible>),
|
||||
Route(Route<B>),
|
||||
}
|
||||
|
||||
impl<B> Clone for Endpoint<B> {
|
||||
impl<S, B> Clone for Endpoint<S, B>
|
||||
where
|
||||
S: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Endpoint::MethodRouter(inner) => Endpoint::MethodRouter(inner.clone()),
|
||||
@@ -559,7 +630,10 @@ impl<B> Clone for Endpoint<B> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> fmt::Debug for Endpoint<B> {
|
||||
impl<S, B> fmt::Debug for Endpoint<S, B>
|
||||
where
|
||||
S: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::MethodRouter(inner) => inner.fmt(f),
|
||||
@@ -572,5 +646,5 @@ impl<B> fmt::Debug for Endpoint<B> {
|
||||
#[allow(warnings)]
|
||||
fn traits() {
|
||||
use crate::test_helpers::*;
|
||||
assert_send::<Router<()>>();
|
||||
assert_send::<Router<(), ()>>();
|
||||
}
|
||||
|
||||
@@ -48,13 +48,13 @@ impl<B, E> Route<B, E> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<ReqBody, E> Clone for Route<ReqBody, E> {
|
||||
impl<B, E> Clone for Route<B, E> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<ReqBody, E> fmt::Debug for Route<ReqBody, E> {
|
||||
impl<B, E> fmt::Debug for Route<B, E> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Route").finish()
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
use super::*;
|
||||
use crate::handler::Handler;
|
||||
|
||||
#[tokio::test]
|
||||
async fn basic() {
|
||||
let app = Router::new()
|
||||
.route("/foo", get(|| async {}))
|
||||
.fallback((|| async { "fallback" }).into_service());
|
||||
.fallback(|| async { "fallback" });
|
||||
|
||||
let client = TestClient::new(app);
|
||||
|
||||
@@ -20,7 +19,7 @@ async fn basic() {
|
||||
async fn nest() {
|
||||
let app = Router::new()
|
||||
.nest("/foo", Router::new().route("/bar", get(|| async {})))
|
||||
.fallback((|| async { "fallback" }).into_service());
|
||||
.fallback(|| async { "fallback" });
|
||||
|
||||
let client = TestClient::new(app);
|
||||
|
||||
@@ -36,9 +35,7 @@ async fn or() {
|
||||
let one = Router::new().route("/one", get(|| async {}));
|
||||
let two = Router::new().route("/two", get(|| async {}));
|
||||
|
||||
let app = one
|
||||
.merge(two)
|
||||
.fallback((|| async { "fallback" }).into_service());
|
||||
let app = one.merge(two).fallback(|| async { "fallback" });
|
||||
|
||||
let client = TestClient::new(app);
|
||||
|
||||
@@ -49,3 +46,15 @@ async fn or() {
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
assert_eq!(res.text().await, "fallback");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn fallback_accessing_state() {
|
||||
let app = Router::with_state("state")
|
||||
.fallback(|State(state): State<&'static str>| async move { state });
|
||||
|
||||
let client = TestClient::new(app);
|
||||
|
||||
let res = client.get("/does-not-exist").send().await;
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
assert_eq!(res.text().await, "state");
|
||||
}
|
||||
|
||||
@@ -408,3 +408,72 @@ async fn middleware_that_return_early() {
|
||||
);
|
||||
assert_eq!(client.get("/public").send().await.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn merge_with_different_state_type() {
|
||||
let inner = Router::with_state("inner".to_owned()).route(
|
||||
"/foo",
|
||||
get(|State(state): State<String>| async move { state }),
|
||||
);
|
||||
|
||||
let app = Router::with_state("outer").merge(inner).route(
|
||||
"/bar",
|
||||
get(|State(state): State<&'static str>| async move { state }),
|
||||
);
|
||||
|
||||
let client = TestClient::new(app);
|
||||
|
||||
let res = client.get("/foo").send().await;
|
||||
assert_eq!(res.text().await, "inner");
|
||||
|
||||
let res = client.get("/bar").send().await;
|
||||
assert_eq!(res.text().await, "outer");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn merging_routes_different_method_different_states() {
|
||||
let get = Router::with_state("get state").route(
|
||||
"/",
|
||||
get(|State(state): State<&'static str>| async move { state }),
|
||||
);
|
||||
|
||||
let post = Router::with_state("post state").route(
|
||||
"/",
|
||||
post(|State(state): State<&'static str>| async move { state }),
|
||||
);
|
||||
|
||||
let app = Router::new().merge(get).merge(post);
|
||||
|
||||
let client = TestClient::new(app);
|
||||
|
||||
let res = client.get("/").send().await;
|
||||
assert_eq!(res.text().await, "get state");
|
||||
|
||||
let res = client.post("/").send().await;
|
||||
assert_eq!(res.text().await, "post state");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn merging_routes_different_paths_different_states() {
|
||||
let foo = Router::with_state("foo state").route(
|
||||
"/foo",
|
||||
get(|State(state): State<&'static str>| async move { state }),
|
||||
);
|
||||
|
||||
let bar = Router::with_state("bar state").route(
|
||||
"/bar",
|
||||
get(|State(state): State<&'static str>| async move { state }),
|
||||
);
|
||||
|
||||
let app = Router::new().merge(foo).merge(bar);
|
||||
|
||||
let client = TestClient::new(app);
|
||||
|
||||
let res = client.get("/foo").send().await;
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
assert_eq!(res.text().await, "foo state");
|
||||
|
||||
let res = client.get("/bar").send().await;
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
assert_eq!(res.text().await, "bar state");
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::{
|
||||
body::{Bytes, Empty},
|
||||
error_handling::HandleErrorLayer,
|
||||
extract::{self, Path},
|
||||
handler::Handler,
|
||||
extract::{self, FromRef, Path, State},
|
||||
handler::{Handler, HandlerWithoutStateExt},
|
||||
response::IntoResponse,
|
||||
routing::{delete, get, get_service, on, on_service, patch, patch_service, post, MethodFilter},
|
||||
test_helpers::*,
|
||||
@@ -444,10 +444,10 @@ async fn middleware_still_run_for_unmatched_requests() {
|
||||
|
||||
#[tokio::test]
|
||||
#[should_panic(
|
||||
expected = "Invalid route: `Router::route` cannot be used with `Router`s. Use `Router::nest` instead"
|
||||
expected = "Invalid route: `Router::route_service` cannot be used with `Router`s. Use `Router::nest` instead"
|
||||
)]
|
||||
async fn routing_to_router_panics() {
|
||||
TestClient::new(Router::new().route("/", Router::new()));
|
||||
TestClient::new(Router::new().route_service("/", Router::new()));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -499,8 +499,8 @@ async fn different_methods_added_in_different_routes() {
|
||||
#[should_panic(expected = "Cannot merge two `Router`s that both have a fallback")]
|
||||
async fn merging_routers_with_fallbacks_panics() {
|
||||
async fn fallback() {}
|
||||
let one = Router::new().fallback(fallback.into_service());
|
||||
let two = Router::new().fallback(fallback.into_service());
|
||||
let one = Router::new().fallback(fallback);
|
||||
let two = Router::new().fallback(fallback);
|
||||
TestClient::new(one.merge(two));
|
||||
}
|
||||
|
||||
@@ -539,7 +539,7 @@ async fn head_content_length_through_hyper_server() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn head_content_length_through_hyper_server_that_hits_fallback() {
|
||||
let app = Router::new().fallback((|| async { "foo" }).into_service());
|
||||
let app = Router::new().fallback(|| async { "foo" });
|
||||
|
||||
let client = TestClient::new(app);
|
||||
|
||||
@@ -641,6 +641,54 @@ async fn limited_body_with_streaming_body() {
|
||||
assert_eq!(res.status(), StatusCode::PAYLOAD_TOO_LARGE);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn extract_state() {
|
||||
#[derive(Clone)]
|
||||
struct AppState {
|
||||
value: i32,
|
||||
inner: InnerState,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct InnerState {
|
||||
value: i32,
|
||||
}
|
||||
|
||||
impl FromRef<AppState> for InnerState {
|
||||
fn from_ref(state: &AppState) -> Self {
|
||||
state.inner.clone()
|
||||
}
|
||||
}
|
||||
|
||||
async fn handler(State(outer): State<AppState>, State(inner): State<InnerState>) {
|
||||
assert_eq!(outer.value, 1);
|
||||
assert_eq!(inner.value, 2);
|
||||
}
|
||||
|
||||
let state = AppState {
|
||||
value: 1,
|
||||
inner: InnerState { value: 2 },
|
||||
};
|
||||
|
||||
let app = Router::with_state(state).route("/", get(handler));
|
||||
let client = TestClient::new(app);
|
||||
|
||||
let res = client.get("/").send().await;
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn explicitly_set_state() {
|
||||
let app = Router::with_state("...").route_service(
|
||||
"/",
|
||||
get(|State(state): State<&'static str>| async move { state }).with_state("foo"),
|
||||
);
|
||||
|
||||
let client = TestClient::new(app);
|
||||
let res = client.get("/").send().await;
|
||||
assert_eq!(res.text().await, "foo");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn layer_response_into_response() {
|
||||
fn map_response<B>(_res: Response<B>) -> Result<Response<B>, impl IntoResponse> {
|
||||
|
||||
@@ -182,7 +182,7 @@ async fn nested_service_sees_stripped_uri() {
|
||||
"/foo",
|
||||
Router::new().nest(
|
||||
"/bar",
|
||||
Router::new().route(
|
||||
Router::new().route_service(
|
||||
"/baz",
|
||||
service_fn(|req: Request<Body>| async move {
|
||||
let body = boxed(Body::from(req.uri().to_string()));
|
||||
@@ -264,7 +264,7 @@ async fn multiple_top_level_nests() {
|
||||
#[tokio::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::<_, Body>::new().nest("/one/*rest", Router::new());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -275,7 +275,7 @@ async fn outer_middleware_still_see_whole_url() {
|
||||
#[derive(Clone)]
|
||||
struct Uri(http::Uri);
|
||||
|
||||
impl<B, S> Service<Request<B>> for SetUriExtension<S>
|
||||
impl<S, B> Service<Request<B>> for SetUriExtension<S>
|
||||
where
|
||||
S: Service<Request<B>>,
|
||||
{
|
||||
@@ -303,7 +303,7 @@ async fn outer_middleware_still_see_whole_url() {
|
||||
.route("/foo", get(handler))
|
||||
.route("/foo/bar", get(handler))
|
||||
.nest("/one", Router::new().route("/two", get(handler)))
|
||||
.fallback(handler.into_service())
|
||||
.fallback(handler)
|
||||
.layer(tower::layer::layer_fn(SetUriExtension));
|
||||
|
||||
let client = TestClient::new(app);
|
||||
@@ -356,7 +356,7 @@ async fn nest_with_and_without_trailing() {
|
||||
async fn doesnt_call_outer_fallback() {
|
||||
let app = Router::new()
|
||||
.nest("/foo", Router::new().route("/", get(|| async {})))
|
||||
.fallback((|| async { (StatusCode::NOT_FOUND, "outer fallback") }).into_service());
|
||||
.fallback(|| async { (StatusCode::NOT_FOUND, "outer fallback") });
|
||||
|
||||
let client = TestClient::new(app);
|
||||
|
||||
@@ -396,9 +396,9 @@ async fn fallback_on_inner() {
|
||||
"/foo",
|
||||
Router::new()
|
||||
.route("/", get(|| async {}))
|
||||
.fallback((|| async { (StatusCode::NOT_FOUND, "inner fallback") }).into_service()),
|
||||
.fallback(|| async { (StatusCode::NOT_FOUND, "inner fallback") }),
|
||||
)
|
||||
.fallback((|| async { (StatusCode::NOT_FOUND, "outer fallback") }).into_service());
|
||||
.fallback(|| async { (StatusCode::NOT_FOUND, "outer fallback") });
|
||||
|
||||
let client = TestClient::new(app);
|
||||
|
||||
@@ -442,3 +442,33 @@ nested_route_test!(nest_9, nest = "/a", route = "/a/", expected = "/a/a/");
|
||||
nested_route_test!(nest_11, nest = "/a/", route = "/", expected = "/a/");
|
||||
nested_route_test!(nest_12, nest = "/a/", route = "/a", expected = "/a/a");
|
||||
nested_route_test!(nest_13, nest = "/a/", route = "/a/", expected = "/a/a/");
|
||||
|
||||
#[tokio::test]
|
||||
async fn nesting_with_different_state() {
|
||||
let inner = Router::with_state("inner".to_owned()).route(
|
||||
"/foo",
|
||||
get(|State(state): State<String>| async move { state }),
|
||||
);
|
||||
|
||||
let outer = Router::with_state("outer")
|
||||
.route(
|
||||
"/foo",
|
||||
get(|State(state): State<&'static str>| async move { state }),
|
||||
)
|
||||
.nest("/nested", inner)
|
||||
.route(
|
||||
"/bar",
|
||||
get(|State(state): State<&'static str>| async move { state }),
|
||||
);
|
||||
|
||||
let client = TestClient::new(outer);
|
||||
|
||||
let res = client.get("/foo").send().await;
|
||||
assert_eq!(res.text().await, "outer");
|
||||
|
||||
let res = client.get("/nested/foo").send().await;
|
||||
assert_eq!(res.text().await, "inner");
|
||||
|
||||
let res = client.get("/bar").send().await;
|
||||
assert_eq!(res.text().await, "outer");
|
||||
}
|
||||
|
||||
@@ -52,14 +52,15 @@ use std::{convert::Infallible, ops::Deref};
|
||||
pub struct TypedHeader<T>(pub T);
|
||||
|
||||
#[async_trait]
|
||||
impl<T, B> FromRequest<B> for TypedHeader<T>
|
||||
impl<T, S, B> FromRequest<S, B> for TypedHeader<T>
|
||||
where
|
||||
T: headers::Header,
|
||||
B: Send,
|
||||
S: Send,
|
||||
{
|
||||
type Rejection = TypedHeaderRejection;
|
||||
|
||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
match req.headers().typed_try_get::<T>() {
|
||||
Ok(Some(value)) => Ok(Self(value)),
|
||||
Ok(None) => Err(TypedHeaderRejection {
|
||||
|
||||
Reference in New Issue
Block a user