mirror of
https://github.com/tokio-rs/axum.git
synced 2025-04-10 08:03:04 +02:00
Reorganize method routers for handlers and services (#405)
* Re-organize method routing for handlers * Re-organize method routing for services * changelog
This commit is contained in:
parent
0ee7379d4f
commit
7692baf837
57 changed files with 743 additions and 742 deletions
CHANGELOG.md
examples
async-graphql/src
chat/src
customize-extractor-error/src
error-handling-and-dependency-injection/src
form/src
global-404-handler/src
graceful_shutdown/src
hello-world/src
jwt/src
key-value-store/src
low-level-rustls/src
multipart-form/src
oauth/src
print-request-response/src
reverse-proxy/src
sessions/src
sse/src
static-file-server/src
templates/src
testing/src
todos/src
tokio-postgres/src
tracing-aka-logging/src
unix-domain-socket/src
validator/src
versioning/src
websockets/src
src
body
extract
connect_info.rscontent_length_limit.rsextension.rsextractor_middleware.rsform.rsmod.rsmultipart.rs
path
query.rsraw_query.rsrequest_parts.rstyped_header.rsws.rshandler
json.rslib.rsresponse
routing
service
tests
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -56,7 +56,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
```rust,no_run
|
||||
use axum::{
|
||||
handler::get,
|
||||
routing::get,
|
||||
http::StatusCode,
|
||||
error_handling::HandleErrorLayer,
|
||||
response::IntoResponse,
|
||||
|
@ -89,7 +89,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
use axum::{
|
||||
Router, service,
|
||||
body::Body,
|
||||
handler::get,
|
||||
routing::service_method_router::get,
|
||||
response::IntoResponse,
|
||||
http::{Request, Response},
|
||||
error_handling::HandleErrorExt, // for `.handle_error`
|
||||
|
@ -100,7 +100,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
let app = Router::new()
|
||||
.route(
|
||||
"/",
|
||||
service::get(service_fn(|_req: Request<Body>| async {
|
||||
get(service_fn(|_req: Request<Body>| async {
|
||||
let contents = tokio::fs::read_to_string("some_file").await?;
|
||||
Ok::<_, io::Error>(Response::new(Body::from(contents)))
|
||||
}))
|
||||
|
@ -111,6 +111,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
// ...
|
||||
}
|
||||
```
|
||||
- **breaking:** Method routing for handlers have been moved from `axum::handler`
|
||||
to `axum::routing`. So `axum::handler::get` now lives at `axum::routing::get`
|
||||
([#405])
|
||||
- **breaking:** Method routing for services have been moved from `axum::service`
|
||||
to `axum::routing`. So `axum::service::get` now lives at
|
||||
`axum::service_method_router::get` ([#405])
|
||||
|
||||
[#339]: https://github.com/tokio-rs/axum/pull/339
|
||||
[#286]: https://github.com/tokio-rs/axum/pull/286
|
||||
|
@ -120,6 +126,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
[#396]: https://github.com/tokio-rs/axum/pull/396
|
||||
[#402]: https://github.com/tokio-rs/axum/pull/402
|
||||
[#404]: https://github.com/tokio-rs/axum/pull/404
|
||||
[#405]: https://github.com/tokio-rs/axum/pull/405
|
||||
|
||||
# 0.2.8 (07. October, 2021)
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ mod starwars;
|
|||
use async_graphql::http::{playground_source, GraphQLPlaygroundConfig};
|
||||
use async_graphql::{EmptyMutation, EmptySubscription, Request, Response, Schema};
|
||||
use axum::response::IntoResponse;
|
||||
use axum::{extract::Extension, handler::get, response::Html, AddExtensionLayer, Json, Router};
|
||||
use axum::{extract::Extension, response::Html, routing::get, AddExtensionLayer, Json, Router};
|
||||
use starwars::{QueryRoot, StarWars, StarWarsSchema};
|
||||
|
||||
async fn graphql_handler(schema: Extension<StarWarsSchema>, req: Json<Request>) -> Json<Response> {
|
||||
|
|
|
@ -11,8 +11,8 @@ use axum::{
|
|||
ws::{Message, WebSocket, WebSocketUpgrade},
|
||||
Extension,
|
||||
},
|
||||
handler::get,
|
||||
response::{Html, IntoResponse},
|
||||
routing::get,
|
||||
AddExtensionLayer, Router,
|
||||
};
|
||||
use futures::{sink::SinkExt, stream::StreamExt};
|
||||
|
|
|
@ -8,8 +8,8 @@ use axum::{
|
|||
async_trait,
|
||||
extract::rejection::JsonRejection,
|
||||
extract::{FromRequest, RequestParts},
|
||||
handler::post,
|
||||
http::StatusCode,
|
||||
routing::post,
|
||||
BoxError, Router,
|
||||
};
|
||||
use serde::{de::DeserializeOwned, Deserialize};
|
||||
|
|
|
@ -11,9 +11,9 @@ use axum::{
|
|||
async_trait,
|
||||
body::{Bytes, Full},
|
||||
extract::{Extension, Path},
|
||||
handler::{get, post},
|
||||
http::{Response, StatusCode},
|
||||
response::IntoResponse,
|
||||
routing::{get, post},
|
||||
AddExtensionLayer, Json, Router,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
//! cargo run -p example-form
|
||||
//! ```
|
||||
|
||||
use axum::{extract::Form, handler::get, response::Html, Router};
|
||||
use axum::{extract::Form, response::Html, routing::get, Router};
|
||||
use serde::Deserialize;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
//! ```
|
||||
|
||||
use axum::{
|
||||
handler::{get, Handler},
|
||||
handler::Handler,
|
||||
http::StatusCode,
|
||||
response::{Html, IntoResponse},
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use std::net::SocketAddr;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
//! kill or ctrl-c
|
||||
//! ```
|
||||
|
||||
use axum::{handler::get, response::Html, Router};
|
||||
use axum::{response::Html, routing::get, Router};
|
||||
use std::net::SocketAddr;
|
||||
|
||||
#[tokio::main]
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
//! cargo run -p example-hello-world
|
||||
//! ```
|
||||
|
||||
use axum::{handler::get, response::Html, Router};
|
||||
use axum::{response::Html, routing::get, Router};
|
||||
use std::net::SocketAddr;
|
||||
|
||||
#[tokio::main]
|
||||
|
|
|
@ -10,9 +10,9 @@ use axum::{
|
|||
async_trait,
|
||||
body::{Bytes, Full},
|
||||
extract::{FromRequest, RequestParts, TypedHeader},
|
||||
handler::{get, post},
|
||||
http::{Response, StatusCode},
|
||||
response::IntoResponse,
|
||||
routing::{get, post},
|
||||
Json, Router,
|
||||
};
|
||||
use headers::{authorization::Bearer, Authorization};
|
||||
|
|
|
@ -10,9 +10,10 @@ use axum::{
|
|||
body::Bytes,
|
||||
error_handling::HandleErrorLayer,
|
||||
extract::{ContentLengthLimit, Extension, Path},
|
||||
handler::{delete, get, Handler},
|
||||
handler::Handler,
|
||||
http::StatusCode,
|
||||
response::IntoResponse,
|
||||
routing::{delete, get},
|
||||
Router,
|
||||
};
|
||||
use std::{
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
//! cargo run -p example-low-level-rustls
|
||||
//! ```
|
||||
|
||||
use axum::{handler::get, Router};
|
||||
use axum::{routing::get, Router};
|
||||
use hyper::server::conn::Http;
|
||||
use std::{fs::File, io::BufReader, sync::Arc};
|
||||
use tokio::net::TcpListener;
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
use axum::{
|
||||
extract::{ContentLengthLimit, Multipart},
|
||||
handler::get,
|
||||
response::Html,
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use std::net::SocketAddr;
|
||||
|
|
|
@ -11,9 +11,9 @@ use axum::{
|
|||
async_trait,
|
||||
body::{Bytes, Empty},
|
||||
extract::{Extension, FromRequest, Query, RequestParts, TypedHeader},
|
||||
handler::get,
|
||||
http::{header::SET_COOKIE, HeaderMap, Response},
|
||||
response::{IntoResponse, Redirect},
|
||||
routing::get,
|
||||
AddExtensionLayer, Router,
|
||||
};
|
||||
use oauth2::{
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
use axum::{
|
||||
body::{Body, BoxBody, Bytes},
|
||||
error_handling::HandleErrorLayer,
|
||||
handler::post,
|
||||
http::{Request, Response, StatusCode},
|
||||
routing::post,
|
||||
Router,
|
||||
};
|
||||
use std::net::SocketAddr;
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
|
||||
use axum::{
|
||||
extract::Extension,
|
||||
handler::get,
|
||||
http::{uri::Uri, Request, Response},
|
||||
routing::get,
|
||||
AddExtensionLayer, Router,
|
||||
};
|
||||
use hyper::{client::HttpConnector, Body};
|
||||
|
|
|
@ -8,13 +8,13 @@ use async_session::{MemoryStore, Session, SessionStore as _};
|
|||
use axum::{
|
||||
async_trait,
|
||||
extract::{Extension, FromRequest, RequestParts},
|
||||
handler::get,
|
||||
http::{
|
||||
self,
|
||||
header::{HeaderMap, HeaderValue},
|
||||
StatusCode,
|
||||
},
|
||||
response::IntoResponse,
|
||||
routing::get,
|
||||
AddExtensionLayer, Router,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
use axum::{
|
||||
error_handling::HandleErrorExt,
|
||||
extract::TypedHeader,
|
||||
handler::get,
|
||||
http::StatusCode,
|
||||
response::sse::{Event, Sse},
|
||||
routing::{get, service_method_router as service},
|
||||
Router,
|
||||
};
|
||||
use futures::stream::{self, Stream};
|
||||
|
@ -25,15 +25,14 @@ async fn main() {
|
|||
}
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let static_files_service = axum::service::get(
|
||||
ServeDir::new("examples/sse/assets").append_index_html_on_directories(true),
|
||||
)
|
||||
.handle_error(|error: std::io::Error| {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Unhandled internal error: {}", error),
|
||||
)
|
||||
});
|
||||
let static_files_service =
|
||||
service::get(ServeDir::new("examples/sse/assets").append_index_html_on_directories(true))
|
||||
.handle_error(|error: std::io::Error| {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Unhandled internal error: {}", error),
|
||||
)
|
||||
});
|
||||
|
||||
// build our application with a route
|
||||
let app = Router::new()
|
||||
|
|
|
@ -4,7 +4,10 @@
|
|||
//! cargo run -p example-static-file-server
|
||||
//! ```
|
||||
|
||||
use axum::{error_handling::HandleErrorExt, http::StatusCode, service, Router};
|
||||
use axum::{
|
||||
error_handling::HandleErrorExt, http::StatusCode, routing::service_method_router as service,
|
||||
Router,
|
||||
};
|
||||
use std::net::SocketAddr;
|
||||
use tower_http::{services::ServeDir, trace::TraceLayer};
|
||||
|
||||
|
|
|
@ -8,9 +8,9 @@ use askama::Template;
|
|||
use axum::{
|
||||
body::{Bytes, Full},
|
||||
extract,
|
||||
handler::get,
|
||||
http::{Response, StatusCode},
|
||||
response::{Html, IntoResponse},
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use std::{convert::Infallible, net::SocketAddr};
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
//! ```
|
||||
|
||||
use axum::{
|
||||
handler::{get, post},
|
||||
routing::{get, post},
|
||||
Json, Router,
|
||||
};
|
||||
use tower_http::trace::TraceLayer;
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
use axum::{
|
||||
error_handling::HandleErrorLayer,
|
||||
extract::{Extension, Path, Query},
|
||||
handler::{get, patch},
|
||||
http::StatusCode,
|
||||
response::IntoResponse,
|
||||
routing::{get, patch},
|
||||
Json, Router,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
use axum::{
|
||||
async_trait,
|
||||
extract::{Extension, FromRequest, RequestParts},
|
||||
handler::get,
|
||||
http::StatusCode,
|
||||
routing::get,
|
||||
AddExtensionLayer, Router,
|
||||
};
|
||||
use bb8::{Pool, PooledConnection};
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
|
||||
use axum::{
|
||||
body::Bytes,
|
||||
handler::get,
|
||||
http::{HeaderMap, Request, Response},
|
||||
response::Html,
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use std::{net::SocketAddr, time::Duration};
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
use axum::{
|
||||
body::Body,
|
||||
extract::connect_info::{self, ConnectInfo},
|
||||
handler::get,
|
||||
http::{Method, Request, StatusCode, Uri},
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use futures::ready;
|
||||
|
|
|
@ -14,9 +14,9 @@ use async_trait::async_trait;
|
|||
use axum::{
|
||||
body::{Bytes, Full},
|
||||
extract::{Form, FromRequest, RequestParts},
|
||||
handler::get,
|
||||
http::{Response, StatusCode},
|
||||
response::{Html, IntoResponse},
|
||||
routing::get,
|
||||
BoxError, Router,
|
||||
};
|
||||
use serde::{de::DeserializeOwned, Deserialize};
|
||||
|
|
|
@ -8,9 +8,9 @@ use axum::{
|
|||
async_trait,
|
||||
body::{Bytes, Full},
|
||||
extract::{FromRequest, Path, RequestParts},
|
||||
handler::get,
|
||||
http::{Response, StatusCode},
|
||||
response::IntoResponse,
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
|
|
|
@ -12,9 +12,9 @@ use axum::{
|
|||
ws::{Message, WebSocket, WebSocketUpgrade},
|
||||
TypedHeader,
|
||||
},
|
||||
handler::get,
|
||||
http::StatusCode,
|
||||
response::IntoResponse,
|
||||
routing::{get, service_method_router as service},
|
||||
Router,
|
||||
};
|
||||
use std::net::SocketAddr;
|
||||
|
@ -35,7 +35,7 @@ async fn main() {
|
|||
let app = Router::new()
|
||||
.nest(
|
||||
"/",
|
||||
axum::service::get(
|
||||
service::get(
|
||||
ServeDir::new("examples/websockets/assets").append_index_html_on_directories(true),
|
||||
)
|
||||
.handle_error(|error: std::io::Error| {
|
||||
|
|
|
@ -26,7 +26,7 @@ pin_project! {
|
|||
/// ```
|
||||
/// use axum::{
|
||||
/// Router,
|
||||
/// handler::get,
|
||||
/// routing::get,
|
||||
/// body::StreamBody,
|
||||
/// response::IntoResponse,
|
||||
/// };
|
||||
|
|
|
@ -134,7 +134,7 @@ where
|
|||
mod tests {
|
||||
use super::*;
|
||||
use crate::Server;
|
||||
use crate::{handler::get, Router};
|
||||
use crate::{routing::get, Router};
|
||||
use std::net::{SocketAddr, TcpListener};
|
||||
|
||||
#[tokio::test]
|
||||
|
|
|
@ -11,7 +11,7 @@ use std::ops::Deref;
|
|||
/// ```rust,no_run
|
||||
/// use axum::{
|
||||
/// extract::ContentLengthLimit,
|
||||
/// handler::post,
|
||||
/// routing::post,
|
||||
/// Router,
|
||||
/// };
|
||||
///
|
||||
|
|
|
@ -12,7 +12,7 @@ use std::ops::Deref;
|
|||
/// use axum::{
|
||||
/// AddExtensionLayer,
|
||||
/// extract::Extension,
|
||||
/// handler::get,
|
||||
/// routing::get,
|
||||
/// Router,
|
||||
/// };
|
||||
/// use std::sync::Arc;
|
||||
|
|
|
@ -38,7 +38,7 @@ use tower_service::Service;
|
|||
/// ```rust
|
||||
/// use axum::{
|
||||
/// extract::{extractor_middleware, FromRequest, RequestParts},
|
||||
/// handler::{get, post},
|
||||
/// routing::{get, post},
|
||||
/// Router,
|
||||
/// };
|
||||
/// use http::StatusCode;
|
||||
|
|
|
@ -16,7 +16,7 @@ use std::ops::Deref;
|
|||
/// ```rust,no_run
|
||||
/// use axum::{
|
||||
/// extract::Form,
|
||||
/// handler::post,
|
||||
/// routing::post,
|
||||
/// Router,
|
||||
/// };
|
||||
/// use serde::Deserialize;
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
//! ```rust,no_run
|
||||
//! use axum::{
|
||||
//! extract::Json,
|
||||
//! handler::{post, Handler},
|
||||
//! routing::post,
|
||||
//! handler::Handler,
|
||||
//! Router,
|
||||
//! };
|
||||
//! use serde::Deserialize;
|
||||
|
@ -39,7 +40,7 @@
|
|||
//! use axum::{
|
||||
//! async_trait,
|
||||
//! extract::{FromRequest, RequestParts},
|
||||
//! handler::get,
|
||||
//! routing::get,
|
||||
//! Router,
|
||||
//! };
|
||||
//! use http::{StatusCode, header::{HeaderValue, USER_AGENT}};
|
||||
|
@ -94,7 +95,7 @@
|
|||
//! use axum::{
|
||||
//! extract::{self, BodyStream},
|
||||
//! body::Body,
|
||||
//! handler::get,
|
||||
//! routing::get,
|
||||
//! http::{header::HeaderMap, Request},
|
||||
//! Router,
|
||||
//! };
|
||||
|
|
|
@ -22,7 +22,7 @@ use std::{
|
|||
/// ```rust,no_run
|
||||
/// use axum::{
|
||||
/// extract::Multipart,
|
||||
/// handler::post,
|
||||
/// routing::post,
|
||||
/// Router,
|
||||
/// };
|
||||
/// use futures::stream::StreamExt;
|
||||
|
|
|
@ -24,7 +24,7 @@ use std::{
|
|||
/// ```rust,no_run
|
||||
/// use axum::{
|
||||
/// extract::Path,
|
||||
/// handler::get,
|
||||
/// routing::get,
|
||||
/// Router,
|
||||
/// };
|
||||
/// use uuid::Uuid;
|
||||
|
@ -46,7 +46,7 @@ use std::{
|
|||
/// ```rust,no_run
|
||||
/// use axum::{
|
||||
/// extract::Path,
|
||||
/// handler::get,
|
||||
/// routing::get,
|
||||
/// Router,
|
||||
/// };
|
||||
/// use uuid::Uuid;
|
||||
|
@ -68,7 +68,7 @@ use std::{
|
|||
/// ```rust,no_run
|
||||
/// use axum::{
|
||||
/// extract::Path,
|
||||
/// handler::get,
|
||||
/// routing::get,
|
||||
/// Router,
|
||||
/// };
|
||||
/// use serde::Deserialize;
|
||||
|
@ -97,7 +97,7 @@ use std::{
|
|||
/// ```rust,no_run
|
||||
/// use axum::{
|
||||
/// extract::Path,
|
||||
/// handler::get,
|
||||
/// routing::get,
|
||||
/// Router,
|
||||
/// };
|
||||
/// use std::collections::HashMap;
|
||||
|
@ -175,7 +175,7 @@ where
|
|||
mod tests {
|
||||
use super::*;
|
||||
use crate::tests::*;
|
||||
use crate::{handler::get, Router};
|
||||
use crate::{routing::get, Router};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[tokio::test]
|
||||
|
|
|
@ -12,7 +12,7 @@ use std::ops::Deref;
|
|||
/// ```rust,no_run
|
||||
/// use axum::{
|
||||
/// extract::Query,
|
||||
/// handler::get,
|
||||
/// routing::get,
|
||||
/// Router,
|
||||
/// };
|
||||
/// use serde::Deserialize;
|
||||
|
|
|
@ -9,7 +9,7 @@ use std::convert::Infallible;
|
|||
/// ```rust,no_run
|
||||
/// use axum::{
|
||||
/// extract::RawQuery,
|
||||
/// handler::get,
|
||||
/// routing::get,
|
||||
/// Router,
|
||||
/// };
|
||||
/// use futures::StreamExt;
|
||||
|
|
|
@ -94,7 +94,7 @@ where
|
|||
///
|
||||
/// ```
|
||||
/// use axum::{
|
||||
/// handler::get,
|
||||
/// routing::get,
|
||||
/// Router,
|
||||
/// extract::OriginalUri,
|
||||
/// http::Uri
|
||||
|
@ -181,7 +181,7 @@ where
|
|||
/// ```rust,no_run
|
||||
/// use axum::{
|
||||
/// extract::BodyStream,
|
||||
/// handler::get,
|
||||
/// routing::get,
|
||||
/// Router,
|
||||
/// };
|
||||
/// use futures::StreamExt;
|
||||
|
@ -252,7 +252,7 @@ fn body_stream_traits() {
|
|||
/// ```rust,no_run
|
||||
/// use axum::{
|
||||
/// extract::RawBody,
|
||||
/// handler::get,
|
||||
/// routing::get,
|
||||
/// Router,
|
||||
/// };
|
||||
/// use futures::StreamExt;
|
||||
|
@ -326,7 +326,7 @@ where
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{body::Body, handler::post, tests::*, Router};
|
||||
use crate::{body::Body, routing::post, tests::*, Router};
|
||||
use http::StatusCode;
|
||||
|
||||
#[tokio::test]
|
||||
|
|
|
@ -13,7 +13,7 @@ use std::{convert::Infallible, ops::Deref};
|
|||
/// ```rust,no_run
|
||||
/// use axum::{
|
||||
/// extract::TypedHeader,
|
||||
/// handler::get,
|
||||
/// routing::get,
|
||||
/// Router,
|
||||
/// };
|
||||
/// use headers::UserAgent;
|
||||
|
@ -140,7 +140,7 @@ impl std::error::Error for TypedHeaderRejection {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{handler::get, response::IntoResponse, tests::*, Router};
|
||||
use crate::{response::IntoResponse, routing::get, tests::*, Router};
|
||||
|
||||
#[tokio::test]
|
||||
async fn typed_header() {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
//! ```
|
||||
//! use axum::{
|
||||
//! extract::ws::{WebSocketUpgrade, WebSocket},
|
||||
//! handler::get,
|
||||
//! routing::get,
|
||||
//! response::IntoResponse,
|
||||
//! Router,
|
||||
//! };
|
||||
|
@ -96,7 +96,7 @@ use tokio_tungstenite::{
|
|||
/// Extractor for establishing WebSocket connections.
|
||||
///
|
||||
/// Note: This extractor requires the request method to be `GET` so it should
|
||||
/// always be used with [`get`](crate::handler::get). Requests with other methods will be
|
||||
/// always be used with [`get`](crate::routing::get). Requests with other methods will be
|
||||
/// rejected.
|
||||
///
|
||||
/// See the [module docs](self) for an example.
|
||||
|
@ -138,7 +138,7 @@ impl WebSocketUpgrade {
|
|||
/// ```
|
||||
/// use axum::{
|
||||
/// extract::ws::{WebSocketUpgrade, WebSocket},
|
||||
/// handler::get,
|
||||
/// routing::get,
|
||||
/// response::IntoResponse,
|
||||
/// Router,
|
||||
/// };
|
||||
|
|
|
@ -1,69 +1,9 @@
|
|||
//! Handler future types.
|
||||
|
||||
use crate::body::{box_body, BoxBody};
|
||||
use crate::util::{Either, EitherProj};
|
||||
use futures_util::{
|
||||
future::{BoxFuture, Map},
|
||||
ready,
|
||||
};
|
||||
use http::{Method, Request, Response};
|
||||
use http_body::Empty;
|
||||
use pin_project_lite::pin_project;
|
||||
use std::{
|
||||
convert::Infallible,
|
||||
fmt,
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use tower::util::Oneshot;
|
||||
use tower_service::Service;
|
||||
|
||||
pin_project! {
|
||||
/// The response future for [`OnMethod`](super::OnMethod).
|
||||
pub struct OnMethodFuture<F, B>
|
||||
where
|
||||
F: Service<Request<B>>
|
||||
{
|
||||
#[pin]
|
||||
pub(super) inner: Either<
|
||||
BoxFuture<'static, Response<BoxBody>>,
|
||||
Oneshot<F, Request<B>>,
|
||||
>,
|
||||
pub(super) req_method: Method,
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, B> Future for OnMethodFuture<F, B>
|
||||
where
|
||||
F: Service<Request<B>, Response = Response<BoxBody>>,
|
||||
{
|
||||
type Output = Result<Response<BoxBody>, F::Error>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = self.project();
|
||||
let response = match this.inner.project() {
|
||||
EitherProj::A { inner } => ready!(inner.poll(cx)),
|
||||
EitherProj::B { inner } => ready!(inner.poll(cx))?,
|
||||
};
|
||||
|
||||
if this.req_method == &Method::HEAD {
|
||||
let response = response.map(|_| box_body(Empty::new()));
|
||||
Poll::Ready(Ok(response))
|
||||
} else {
|
||||
Poll::Ready(Ok(response))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, B> fmt::Debug for OnMethodFuture<F, B>
|
||||
where
|
||||
F: Service<Request<B>>,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("OnMethodFuture").finish()
|
||||
}
|
||||
}
|
||||
use crate::body::BoxBody;
|
||||
use futures_util::future::{BoxFuture, Map};
|
||||
use http::Response;
|
||||
use std::convert::Infallible;
|
||||
|
||||
opaque_future! {
|
||||
/// The response future for [`IntoService`](super::IntoService).
|
||||
|
|
|
@ -4,20 +4,13 @@ use crate::{
|
|||
body::{box_body, BoxBody},
|
||||
extract::{FromRequest, RequestParts},
|
||||
response::IntoResponse,
|
||||
routing::{EmptyRouter, MethodFilter},
|
||||
util::Either,
|
||||
routing::{EmptyRouter, MethodRouter},
|
||||
BoxError,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use bytes::Bytes;
|
||||
use http::{Request, Response};
|
||||
use std::{
|
||||
convert::Infallible,
|
||||
fmt,
|
||||
future::Future,
|
||||
marker::PhantomData,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use std::{fmt, future::Future, marker::PhantomData};
|
||||
use tower::ServiceExt;
|
||||
use tower_layer::Layer;
|
||||
use tower_service::Service;
|
||||
|
@ -27,187 +20,6 @@ mod into_service;
|
|||
|
||||
pub use self::into_service::IntoService;
|
||||
|
||||
/// Route requests with any standard HTTP method to the given handler.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use axum::{
|
||||
/// handler::any,
|
||||
/// Router,
|
||||
/// };
|
||||
///
|
||||
/// async fn handler() {}
|
||||
///
|
||||
/// let app = Router::new().route("/", any(handler));
|
||||
/// # async {
|
||||
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
/// # };
|
||||
/// ```
|
||||
///
|
||||
/// Note that this only accepts the standard HTTP methods. If you need to
|
||||
/// support non-standard methods use [`Handler::into_service`]:
|
||||
///
|
||||
/// ```rust
|
||||
/// use axum::{
|
||||
/// handler::Handler,
|
||||
/// Router,
|
||||
/// };
|
||||
///
|
||||
/// async fn handler() {}
|
||||
///
|
||||
/// let app = Router::new().route("/", handler.into_service());
|
||||
/// # async {
|
||||
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
/// # };
|
||||
/// ```
|
||||
pub fn any<H, B, T>(handler: H) -> OnMethod<H, B, T, EmptyRouter>
|
||||
where
|
||||
H: Handler<B, T>,
|
||||
{
|
||||
on(MethodFilter::all(), handler)
|
||||
}
|
||||
|
||||
/// Route `CONNECT` requests to the given handler.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn connect<H, B, T>(handler: H) -> OnMethod<H, B, T, EmptyRouter>
|
||||
where
|
||||
H: Handler<B, T>,
|
||||
{
|
||||
on(MethodFilter::CONNECT, handler)
|
||||
}
|
||||
|
||||
/// Route `DELETE` requests to the given handler.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn delete<H, B, T>(handler: H) -> OnMethod<H, B, T, EmptyRouter>
|
||||
where
|
||||
H: Handler<B, T>,
|
||||
{
|
||||
on(MethodFilter::DELETE, handler)
|
||||
}
|
||||
|
||||
/// Route `GET` requests to the given handler.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use axum::{
|
||||
/// handler::get,
|
||||
/// Router,
|
||||
/// };
|
||||
///
|
||||
/// async fn handler() {}
|
||||
///
|
||||
/// // Requests to `GET /` will go to `handler`.
|
||||
/// let app = Router::new().route("/", get(handler));
|
||||
/// # async {
|
||||
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
/// # };
|
||||
/// ```
|
||||
///
|
||||
/// Note that `get` routes will also be called for `HEAD` requests but will have
|
||||
/// the response body removed. Make sure to add explicit `HEAD` routes
|
||||
/// afterwards.
|
||||
pub fn get<H, B, T>(handler: H) -> OnMethod<H, B, T, EmptyRouter>
|
||||
where
|
||||
H: Handler<B, T>,
|
||||
{
|
||||
on(MethodFilter::GET | MethodFilter::HEAD, handler)
|
||||
}
|
||||
|
||||
/// Route `HEAD` requests to the given handler.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn head<H, B, T>(handler: H) -> OnMethod<H, B, T, EmptyRouter>
|
||||
where
|
||||
H: Handler<B, T>,
|
||||
{
|
||||
on(MethodFilter::HEAD, handler)
|
||||
}
|
||||
|
||||
/// Route `OPTIONS` requests to the given handler.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn options<H, B, T>(handler: H) -> OnMethod<H, B, T, EmptyRouter>
|
||||
where
|
||||
H: Handler<B, T>,
|
||||
{
|
||||
on(MethodFilter::OPTIONS, handler)
|
||||
}
|
||||
|
||||
/// Route `PATCH` requests to the given handler.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn patch<H, B, T>(handler: H) -> OnMethod<H, B, T, EmptyRouter>
|
||||
where
|
||||
H: Handler<B, T>,
|
||||
{
|
||||
on(MethodFilter::PATCH, handler)
|
||||
}
|
||||
|
||||
/// Route `POST` requests to the given handler.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn post<H, B, T>(handler: H) -> OnMethod<H, B, T, EmptyRouter>
|
||||
where
|
||||
H: Handler<B, T>,
|
||||
{
|
||||
on(MethodFilter::POST, handler)
|
||||
}
|
||||
|
||||
/// Route `PUT` requests to the given handler.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn put<H, B, T>(handler: H) -> OnMethod<H, B, T, EmptyRouter>
|
||||
where
|
||||
H: Handler<B, T>,
|
||||
{
|
||||
on(MethodFilter::PUT, handler)
|
||||
}
|
||||
|
||||
/// Route `TRACE` requests to the given handler.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn trace<H, B, T>(handler: H) -> OnMethod<H, B, T, EmptyRouter>
|
||||
where
|
||||
H: Handler<B, T>,
|
||||
{
|
||||
on(MethodFilter::TRACE, handler)
|
||||
}
|
||||
|
||||
/// Route requests with the given method to the handler.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use axum::{
|
||||
/// handler::on,
|
||||
/// Router,
|
||||
/// routing::MethodFilter,
|
||||
/// };
|
||||
///
|
||||
/// async fn handler() {}
|
||||
///
|
||||
/// // Requests to `POST /` will go to `handler`.
|
||||
/// let app = Router::new().route("/", on(MethodFilter::POST, handler));
|
||||
/// # async {
|
||||
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
/// # };
|
||||
/// ```
|
||||
pub fn on<H, B, T>(method: MethodFilter, handler: H) -> OnMethod<H, B, T, EmptyRouter>
|
||||
where
|
||||
H: Handler<B, T>,
|
||||
{
|
||||
OnMethod {
|
||||
method,
|
||||
handler,
|
||||
fallback: EmptyRouter::method_not_allowed(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
#![allow(unreachable_pub, missing_docs, missing_debug_implementations)]
|
||||
|
||||
|
@ -250,7 +62,8 @@ pub trait Handler<B, T>: Clone + Send + Sized + 'static {
|
|||
///
|
||||
/// ```rust
|
||||
/// use axum::{
|
||||
/// handler::{get, Handler},
|
||||
/// routing::get,
|
||||
/// handler::Handler,
|
||||
/// Router,
|
||||
/// };
|
||||
/// use tower::limit::{ConcurrencyLimitLayer, ConcurrencyLimit};
|
||||
|
@ -265,9 +78,9 @@ pub trait Handler<B, T>: Clone + Send + Sized + 'static {
|
|||
/// ```
|
||||
fn layer<L>(self, layer: L) -> Layered<L::Service, T>
|
||||
where
|
||||
L: Layer<OnMethod<Self, B, T, EmptyRouter>>,
|
||||
L: Layer<MethodRouter<Self, B, T, EmptyRouter>>,
|
||||
{
|
||||
Layered::new(layer.layer(any(self)))
|
||||
Layered::new(layer.layer(crate::routing::any(self)))
|
||||
}
|
||||
|
||||
/// Convert the handler into a [`Service`].
|
||||
|
@ -424,243 +237,9 @@ impl<S, T> Layered<S, T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// A handler [`Service`] that accepts requests based on a [`MethodFilter`] and
|
||||
/// allows chaining additional handlers.
|
||||
pub struct OnMethod<H, B, T, F> {
|
||||
pub(crate) method: MethodFilter,
|
||||
pub(crate) handler: H,
|
||||
pub(crate) fallback: F,
|
||||
pub(crate) _marker: PhantomData<fn() -> (B, T)>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn traits() {
|
||||
use crate::tests::*;
|
||||
assert_send::<OnMethod<(), NotSendSync, NotSendSync, ()>>();
|
||||
assert_sync::<OnMethod<(), NotSendSync, NotSendSync, ()>>();
|
||||
}
|
||||
|
||||
impl<H, B, T, F> fmt::Debug for OnMethod<H, B, T, F>
|
||||
where
|
||||
T: fmt::Debug,
|
||||
F: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("OnMethod")
|
||||
.field("method", &self.method)
|
||||
.field("handler", &format_args!("{}", std::any::type_name::<H>()))
|
||||
.field("fallback", &self.fallback)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, B, T, F> Clone for OnMethod<H, B, T, F>
|
||||
where
|
||||
H: Clone,
|
||||
F: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
method: self.method,
|
||||
handler: self.handler.clone(),
|
||||
fallback: self.fallback.clone(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, B, T, F> Copy for OnMethod<H, B, T, F>
|
||||
where
|
||||
H: Copy,
|
||||
F: Copy,
|
||||
{
|
||||
}
|
||||
|
||||
impl<H, B, T, F> OnMethod<H, B, T, F> {
|
||||
/// Chain an additional handler that will accept all requests regardless of
|
||||
/// its HTTP method.
|
||||
///
|
||||
/// See [`OnMethod::get`] for an example.
|
||||
pub fn any<H2, T2>(self, handler: H2) -> OnMethod<H2, B, T2, Self>
|
||||
where
|
||||
H2: Handler<B, T2>,
|
||||
{
|
||||
self.on(MethodFilter::all(), handler)
|
||||
}
|
||||
|
||||
/// Chain an additional handler that will only accept `CONNECT` requests.
|
||||
///
|
||||
/// See [`OnMethod::get`] for an example.
|
||||
pub fn connect<H2, T2>(self, handler: H2) -> OnMethod<H2, B, T2, Self>
|
||||
where
|
||||
H2: Handler<B, T2>,
|
||||
{
|
||||
self.on(MethodFilter::CONNECT, handler)
|
||||
}
|
||||
|
||||
/// Chain an additional handler that will only accept `DELETE` requests.
|
||||
///
|
||||
/// See [`OnMethod::get`] for an example.
|
||||
pub fn delete<H2, T2>(self, handler: H2) -> OnMethod<H2, B, T2, Self>
|
||||
where
|
||||
H2: Handler<B, T2>,
|
||||
{
|
||||
self.on(MethodFilter::DELETE, handler)
|
||||
}
|
||||
|
||||
/// Chain an additional handler that will only accept `GET` requests.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use axum::{handler::post, Router};
|
||||
///
|
||||
/// async fn handler() {}
|
||||
///
|
||||
/// async fn other_handler() {}
|
||||
///
|
||||
/// // Requests to `GET /` will go to `handler` and `POST /` will go to
|
||||
/// // `other_handler`.
|
||||
/// let app = Router::new().route("/", post(handler).get(other_handler));
|
||||
/// # async {
|
||||
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
/// # };
|
||||
/// ```
|
||||
///
|
||||
/// Note that `get` routes will also be called for `HEAD` requests but will have
|
||||
/// the response body removed. Make sure to add explicit `HEAD` routes
|
||||
/// afterwards.
|
||||
pub fn get<H2, T2>(self, handler: H2) -> OnMethod<H2, B, T2, Self>
|
||||
where
|
||||
H2: Handler<B, T2>,
|
||||
{
|
||||
self.on(MethodFilter::GET | MethodFilter::HEAD, handler)
|
||||
}
|
||||
|
||||
/// Chain an additional handler that will only accept `HEAD` requests.
|
||||
///
|
||||
/// See [`OnMethod::get`] for an example.
|
||||
pub fn head<H2, T2>(self, handler: H2) -> OnMethod<H2, B, T2, Self>
|
||||
where
|
||||
H2: Handler<B, T2>,
|
||||
{
|
||||
self.on(MethodFilter::HEAD, handler)
|
||||
}
|
||||
|
||||
/// Chain an additional handler that will only accept `OPTIONS` requests.
|
||||
///
|
||||
/// See [`OnMethod::get`] for an example.
|
||||
pub fn options<H2, T2>(self, handler: H2) -> OnMethod<H2, B, T2, Self>
|
||||
where
|
||||
H2: Handler<B, T2>,
|
||||
{
|
||||
self.on(MethodFilter::OPTIONS, handler)
|
||||
}
|
||||
|
||||
/// Chain an additional handler that will only accept `PATCH` requests.
|
||||
///
|
||||
/// See [`OnMethod::get`] for an example.
|
||||
pub fn patch<H2, T2>(self, handler: H2) -> OnMethod<H2, B, T2, Self>
|
||||
where
|
||||
H2: Handler<B, T2>,
|
||||
{
|
||||
self.on(MethodFilter::PATCH, handler)
|
||||
}
|
||||
|
||||
/// Chain an additional handler that will only accept `POST` requests.
|
||||
///
|
||||
/// See [`OnMethod::get`] for an example.
|
||||
pub fn post<H2, T2>(self, handler: H2) -> OnMethod<H2, B, T2, Self>
|
||||
where
|
||||
H2: Handler<B, T2>,
|
||||
{
|
||||
self.on(MethodFilter::POST, handler)
|
||||
}
|
||||
|
||||
/// Chain an additional handler that will only accept `PUT` requests.
|
||||
///
|
||||
/// See [`OnMethod::get`] for an example.
|
||||
pub fn put<H2, T2>(self, handler: H2) -> OnMethod<H2, B, T2, Self>
|
||||
where
|
||||
H2: Handler<B, T2>,
|
||||
{
|
||||
self.on(MethodFilter::PUT, handler)
|
||||
}
|
||||
|
||||
/// Chain an additional handler that will only accept `TRACE` requests.
|
||||
///
|
||||
/// See [`OnMethod::get`] for an example.
|
||||
pub fn trace<H2, T2>(self, handler: H2) -> OnMethod<H2, B, T2, Self>
|
||||
where
|
||||
H2: Handler<B, T2>,
|
||||
{
|
||||
self.on(MethodFilter::TRACE, handler)
|
||||
}
|
||||
|
||||
/// Chain an additional handler that will accept requests matching the given
|
||||
/// `MethodFilter`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use axum::{
|
||||
/// handler::get,
|
||||
/// Router,
|
||||
/// routing::MethodFilter
|
||||
/// };
|
||||
///
|
||||
/// async fn handler() {}
|
||||
///
|
||||
/// async fn other_handler() {}
|
||||
///
|
||||
/// // Requests to `GET /` will go to `handler` and `DELETE /` will go to
|
||||
/// // `other_handler`
|
||||
/// let app = Router::new().route("/", get(handler).on(MethodFilter::DELETE, other_handler));
|
||||
/// # async {
|
||||
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
/// # };
|
||||
/// ```
|
||||
pub fn on<H2, T2>(self, method: MethodFilter, handler: H2) -> OnMethod<H2, B, T2, Self>
|
||||
where
|
||||
H2: Handler<B, T2>,
|
||||
{
|
||||
OnMethod {
|
||||
method,
|
||||
handler,
|
||||
fallback: self,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, B, T, F> Service<Request<B>> for OnMethod<H, B, T, F>
|
||||
where
|
||||
H: Handler<B, T>,
|
||||
F: Service<Request<B>, Response = Response<BoxBody>, Error = Infallible> + Clone,
|
||||
B: Send + 'static,
|
||||
{
|
||||
type Response = Response<BoxBody>;
|
||||
type Error = Infallible;
|
||||
type Future = future::OnMethodFuture<F, B>;
|
||||
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request<B>) -> Self::Future {
|
||||
let req_method = req.method().clone();
|
||||
|
||||
let fut = if self.method.matches(req.method()) {
|
||||
let fut = Handler::call(self.handler.clone(), req);
|
||||
Either::A { inner: fut }
|
||||
} else {
|
||||
let fut = self.fallback.clone().oneshot(req);
|
||||
Either::B { inner: fut }
|
||||
};
|
||||
|
||||
future::OnMethodFuture {
|
||||
inner: fut,
|
||||
req_method,
|
||||
}
|
||||
}
|
||||
assert_send::<MethodRouter<(), NotSendSync, NotSendSync, ()>>();
|
||||
assert_sync::<MethodRouter<(), NotSendSync, NotSendSync, ()>>();
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ use std::{
|
|||
/// ```rust,no_run
|
||||
/// use axum::{
|
||||
/// extract,
|
||||
/// handler::post,
|
||||
/// routing::post,
|
||||
/// Router,
|
||||
/// };
|
||||
/// use serde::Deserialize;
|
||||
|
@ -58,7 +58,7 @@ use std::{
|
|||
/// ```
|
||||
/// use axum::{
|
||||
/// extract::Path,
|
||||
/// handler::get,
|
||||
/// routing::get,
|
||||
/// Router,
|
||||
/// Json,
|
||||
/// };
|
||||
|
|
56
src/lib.rs
56
src/lib.rs
|
@ -55,7 +55,7 @@
|
|||
//!
|
||||
//! ```rust,no_run
|
||||
//! use axum::{
|
||||
//! handler::get,
|
||||
//! routing::get,
|
||||
//! Router,
|
||||
//! };
|
||||
//!
|
||||
|
@ -134,7 +134,7 @@
|
|||
//! ::: axum/src/handler/mod.rs:116:8
|
||||
//! |
|
||||
//! 116 | H: Handler<B, T>,
|
||||
//! | ------------- required by this bound in `axum::handler::get`
|
||||
//! | ------------- required by this bound in `axum::routing::get`
|
||||
//! ```
|
||||
//!
|
||||
//! This error doesn't tell you _why_ your function doesn't implement
|
||||
|
@ -147,7 +147,7 @@
|
|||
//!
|
||||
//! ```rust,no_run
|
||||
//! use axum::{
|
||||
//! handler::get,
|
||||
//! routing::get,
|
||||
//! Router,
|
||||
//! };
|
||||
//!
|
||||
|
@ -186,8 +186,8 @@
|
|||
//! ```rust,no_run
|
||||
//! use axum::{
|
||||
//! Router,
|
||||
//! service,
|
||||
//! body::Body,
|
||||
//! routing::service_method_router as service,
|
||||
//! error_handling::HandleErrorExt,
|
||||
//! http::{Request, StatusCode},
|
||||
//! };
|
||||
|
@ -248,8 +248,7 @@
|
|||
//! ```compile_fail
|
||||
//! use axum::{
|
||||
//! Router,
|
||||
//! service,
|
||||
//! handler::get,
|
||||
//! routing::{get, service_method_router as service},
|
||||
//! http::{Request, Response},
|
||||
//! body::Body,
|
||||
//! };
|
||||
|
@ -277,9 +276,9 @@
|
|||
//!
|
||||
//! ```
|
||||
//! use axum::{
|
||||
//! Router, service,
|
||||
//! Router,
|
||||
//! body::Body,
|
||||
//! handler::get,
|
||||
//! routing::{get, service_method_router as service},
|
||||
//! response::IntoResponse,
|
||||
//! http::{Request, Response},
|
||||
//! error_handling::HandleErrorExt,
|
||||
|
@ -321,7 +320,7 @@
|
|||
//!
|
||||
//! ```rust,no_run
|
||||
//! use axum::{
|
||||
//! handler::get,
|
||||
//! routing::get,
|
||||
//! Router,
|
||||
//! };
|
||||
//!
|
||||
|
@ -337,7 +336,7 @@
|
|||
//!
|
||||
//! ```rust,no_run
|
||||
//! use axum::{
|
||||
//! handler::get,
|
||||
//! routing::get,
|
||||
//! extract::Path,
|
||||
//! Router,
|
||||
//! };
|
||||
|
@ -358,7 +357,7 @@
|
|||
//! use axum::{
|
||||
//! body::{Body, BoxBody},
|
||||
//! http::Request,
|
||||
//! handler::get,
|
||||
//! routing::get,
|
||||
//! Router,
|
||||
//! };
|
||||
//! use tower_http::services::ServeFile;
|
||||
|
@ -387,7 +386,7 @@
|
|||
//! the prefix stripped.
|
||||
//!
|
||||
//! ```rust
|
||||
//! use axum::{handler::get, http::Uri, Router};
|
||||
//! use axum::{routing::get, http::Uri, Router};
|
||||
//!
|
||||
//! let app = Router::new()
|
||||
//! .route("/foo/*rest", get(|uri: Uri| async {
|
||||
|
@ -412,7 +411,7 @@
|
|||
//! ```rust,no_run
|
||||
//! use axum::{
|
||||
//! extract::Json,
|
||||
//! handler::post,
|
||||
//! routing::post,
|
||||
//! Router,
|
||||
//! };
|
||||
//! use serde::Deserialize;
|
||||
|
@ -442,7 +441,7 @@
|
|||
//! ```rust,no_run
|
||||
//! use axum::{
|
||||
//! extract::{Json, TypedHeader, Path, Extension, Query},
|
||||
//! handler::post,
|
||||
//! routing::post,
|
||||
//! http::{Request, header::HeaderMap},
|
||||
//! body::{Bytes, Body},
|
||||
//! Router,
|
||||
|
@ -506,7 +505,7 @@
|
|||
//! ```rust,no_run
|
||||
//! use axum::{
|
||||
//! extract,
|
||||
//! handler::get,
|
||||
//! routing::get,
|
||||
//! Router,
|
||||
//! };
|
||||
//! use uuid::Uuid;
|
||||
|
@ -550,7 +549,7 @@
|
|||
//! ```rust,no_run
|
||||
//! use axum::{
|
||||
//! extract::TypedHeader,
|
||||
//! handler::get,
|
||||
//! routing::get,
|
||||
//! http::header::HeaderMap,
|
||||
//! Router,
|
||||
//! };
|
||||
|
@ -574,7 +573,7 @@
|
|||
//!
|
||||
//! ```rust,no_run
|
||||
//! use axum::{
|
||||
//! handler::get,
|
||||
//! routing::get,
|
||||
//! http::Request,
|
||||
//! body::Body,
|
||||
//! Router,
|
||||
|
@ -601,7 +600,7 @@
|
|||
//! ```rust,no_run
|
||||
//! use axum::{
|
||||
//! extract::Json,
|
||||
//! handler::post,
|
||||
//! routing::post,
|
||||
//! Router,
|
||||
//! };
|
||||
//! use serde_json::Value;
|
||||
|
@ -626,7 +625,7 @@
|
|||
//! ```rust,no_run
|
||||
//! use axum::{
|
||||
//! extract::{Json, rejection::JsonRejection},
|
||||
//! handler::post,
|
||||
//! routing::post,
|
||||
//! Router,
|
||||
//! };
|
||||
//! use serde_json::Value;
|
||||
|
@ -680,7 +679,8 @@
|
|||
//! ```rust,no_run
|
||||
//! use axum::{
|
||||
//! body::Body,
|
||||
//! handler::{get, Handler},
|
||||
//! routing::get,
|
||||
//! handler::Handler,
|
||||
//! http::{Request, header::{HeaderMap, HeaderName, HeaderValue}},
|
||||
//! response::{IntoResponse, Html, Json, Headers},
|
||||
//! Router,
|
||||
|
@ -819,7 +819,8 @@
|
|||
//!
|
||||
//! ```rust,no_run
|
||||
//! use axum::{
|
||||
//! handler::{get, Handler},
|
||||
//! handler::Handler,
|
||||
//! routing::get,
|
||||
//! Router,
|
||||
//! };
|
||||
//! use tower::limit::ConcurrencyLimitLayer;
|
||||
|
@ -842,7 +843,7 @@
|
|||
//!
|
||||
//! ```rust,no_run
|
||||
//! use axum::{
|
||||
//! handler::{get, post},
|
||||
//! routing::{get, post},
|
||||
//! Router,
|
||||
//! };
|
||||
//! use tower::limit::ConcurrencyLimitLayer;
|
||||
|
@ -865,7 +866,7 @@
|
|||
//!
|
||||
//! ```rust,no_run
|
||||
//! use axum::{
|
||||
//! handler::{get, post},
|
||||
//! routing::{get, post},
|
||||
//! Router,
|
||||
//! };
|
||||
//! use tower::limit::ConcurrencyLimitLayer;
|
||||
|
@ -895,7 +896,7 @@
|
|||
//! ```rust,no_run
|
||||
//! use axum::{
|
||||
//! body::Body,
|
||||
//! handler::get,
|
||||
//! routing::get,
|
||||
//! http::{Request, StatusCode},
|
||||
//! error_handling::HandleErrorLayer,
|
||||
//! response::IntoResponse,
|
||||
|
@ -945,7 +946,7 @@
|
|||
//! ```rust,no_run
|
||||
//! use axum::{
|
||||
//! body::{Body, BoxBody},
|
||||
//! handler::get,
|
||||
//! routing::get,
|
||||
//! http::{Request, Response},
|
||||
//! error_handling::HandleErrorLayer,
|
||||
//! Router,
|
||||
|
@ -1004,7 +1005,7 @@
|
|||
//! ```
|
||||
//! use axum::{
|
||||
//! body::{Body, BoxBody},
|
||||
//! handler::get,
|
||||
//! routing::get,
|
||||
//! http::{Request, Response},
|
||||
//! Router,
|
||||
//! };
|
||||
|
@ -1069,7 +1070,7 @@
|
|||
//! use axum::{
|
||||
//! AddExtensionLayer,
|
||||
//! extract,
|
||||
//! handler::get,
|
||||
//! routing::get,
|
||||
//! Router,
|
||||
//! };
|
||||
//! use std::sync::Arc;
|
||||
|
@ -1220,7 +1221,6 @@ pub mod extract;
|
|||
pub mod handler;
|
||||
pub mod response;
|
||||
pub mod routing;
|
||||
pub mod service;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
|
|
@ -18,7 +18,7 @@ use tower::util::Either;
|
|||
/// use axum::{
|
||||
/// Router,
|
||||
/// response::{IntoResponse, Headers},
|
||||
/// handler::get,
|
||||
/// routing::get,
|
||||
/// };
|
||||
/// use http::header::{HeaderName, HeaderValue};
|
||||
///
|
||||
|
|
|
@ -40,7 +40,7 @@ pub use self::{headers::Headers, redirect::Redirect, sse::Sse};
|
|||
/// use axum::{
|
||||
/// Router,
|
||||
/// body::Body,
|
||||
/// handler::get,
|
||||
/// routing::get,
|
||||
/// http::{Response, StatusCode},
|
||||
/// response::IntoResponse,
|
||||
/// };
|
||||
|
@ -87,7 +87,7 @@ pub use self::{headers::Headers, redirect::Redirect, sse::Sse};
|
|||
///
|
||||
/// ```rust
|
||||
/// use axum::{
|
||||
/// handler::get,
|
||||
/// routing::get,
|
||||
/// response::IntoResponse,
|
||||
/// Router,
|
||||
/// };
|
||||
|
|
|
@ -10,7 +10,7 @@ use std::convert::TryFrom;
|
|||
///
|
||||
/// ```rust
|
||||
/// use axum::{
|
||||
/// handler::get,
|
||||
/// routing::get,
|
||||
/// response::Redirect,
|
||||
/// Router,
|
||||
/// };
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
//! ```
|
||||
//! use axum::{
|
||||
//! Router,
|
||||
//! handler::get,
|
||||
//! routing::get,
|
||||
//! response::sse::{Event, KeepAlive, Sse},
|
||||
//! };
|
||||
//! use std::{time::Duration, convert::Infallible};
|
||||
|
|
485
src/routing/handler_method_router.rs
Normal file
485
src/routing/handler_method_router.rs
Normal file
|
@ -0,0 +1,485 @@
|
|||
//! Routing for handlers based on HTTP methods.
|
||||
|
||||
use crate::{
|
||||
body::{box_body, BoxBody},
|
||||
handler::Handler,
|
||||
routing::{EmptyRouter, MethodFilter},
|
||||
util::{Either, EitherProj},
|
||||
};
|
||||
use futures_util::{future::BoxFuture, ready};
|
||||
use http::Method;
|
||||
use http::{Request, Response};
|
||||
use http_body::Empty;
|
||||
use pin_project_lite::pin_project;
|
||||
use std::{
|
||||
convert::Infallible,
|
||||
fmt,
|
||||
future::Future,
|
||||
marker::PhantomData,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use tower::util::Oneshot;
|
||||
use tower::ServiceExt;
|
||||
use tower_service::Service;
|
||||
|
||||
/// Route requests with any standard HTTP method to the given handler.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use axum::{
|
||||
/// routing::any,
|
||||
/// Router,
|
||||
/// };
|
||||
///
|
||||
/// async fn handler() {}
|
||||
///
|
||||
/// let app = Router::new().route("/", any(handler));
|
||||
/// # async {
|
||||
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
/// # };
|
||||
/// ```
|
||||
///
|
||||
/// Note that this only accepts the standard HTTP methods. If you need to
|
||||
/// support non-standard methods use [`Handler::into_service`]:
|
||||
///
|
||||
/// ```rust
|
||||
/// use axum::{
|
||||
/// handler::Handler,
|
||||
/// Router,
|
||||
/// };
|
||||
///
|
||||
/// async fn handler() {}
|
||||
///
|
||||
/// let app = Router::new().route("/", handler.into_service());
|
||||
/// # async {
|
||||
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
/// # };
|
||||
/// ```
|
||||
pub fn any<H, B, T>(handler: H) -> MethodRouter<H, B, T, EmptyRouter>
|
||||
where
|
||||
H: Handler<B, T>,
|
||||
{
|
||||
on(MethodFilter::all(), handler)
|
||||
}
|
||||
|
||||
/// Route `CONNECT` requests to the given handler.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn connect<H, B, T>(handler: H) -> MethodRouter<H, B, T, EmptyRouter>
|
||||
where
|
||||
H: Handler<B, T>,
|
||||
{
|
||||
on(MethodFilter::CONNECT, handler)
|
||||
}
|
||||
|
||||
/// Route `DELETE` requests to the given handler.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn delete<H, B, T>(handler: H) -> MethodRouter<H, B, T, EmptyRouter>
|
||||
where
|
||||
H: Handler<B, T>,
|
||||
{
|
||||
on(MethodFilter::DELETE, handler)
|
||||
}
|
||||
|
||||
/// Route `GET` requests to the given handler.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use axum::{
|
||||
/// routing::get,
|
||||
/// Router,
|
||||
/// };
|
||||
///
|
||||
/// async fn handler() {}
|
||||
///
|
||||
/// // Requests to `GET /` will go to `handler`.
|
||||
/// let app = Router::new().route("/", get(handler));
|
||||
/// # async {
|
||||
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
/// # };
|
||||
/// ```
|
||||
///
|
||||
/// Note that `get` routes will also be called for `HEAD` requests but will have
|
||||
/// the response body removed. Make sure to add explicit `HEAD` routes
|
||||
/// afterwards.
|
||||
pub fn get<H, B, T>(handler: H) -> MethodRouter<H, B, T, EmptyRouter>
|
||||
where
|
||||
H: Handler<B, T>,
|
||||
{
|
||||
on(MethodFilter::GET | MethodFilter::HEAD, handler)
|
||||
}
|
||||
|
||||
/// Route `HEAD` requests to the given handler.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn head<H, B, T>(handler: H) -> MethodRouter<H, B, T, EmptyRouter>
|
||||
where
|
||||
H: Handler<B, T>,
|
||||
{
|
||||
on(MethodFilter::HEAD, handler)
|
||||
}
|
||||
|
||||
/// Route `OPTIONS` requests to the given handler.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn options<H, B, T>(handler: H) -> MethodRouter<H, B, T, EmptyRouter>
|
||||
where
|
||||
H: Handler<B, T>,
|
||||
{
|
||||
on(MethodFilter::OPTIONS, handler)
|
||||
}
|
||||
|
||||
/// Route `PATCH` requests to the given handler.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn patch<H, B, T>(handler: H) -> MethodRouter<H, B, T, EmptyRouter>
|
||||
where
|
||||
H: Handler<B, T>,
|
||||
{
|
||||
on(MethodFilter::PATCH, handler)
|
||||
}
|
||||
|
||||
/// Route `POST` requests to the given handler.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn post<H, B, T>(handler: H) -> MethodRouter<H, B, T, EmptyRouter>
|
||||
where
|
||||
H: Handler<B, T>,
|
||||
{
|
||||
on(MethodFilter::POST, handler)
|
||||
}
|
||||
|
||||
/// Route `PUT` requests to the given handler.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn put<H, B, T>(handler: H) -> MethodRouter<H, B, T, EmptyRouter>
|
||||
where
|
||||
H: Handler<B, T>,
|
||||
{
|
||||
on(MethodFilter::PUT, handler)
|
||||
}
|
||||
|
||||
/// Route `TRACE` requests to the given handler.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn trace<H, B, T>(handler: H) -> MethodRouter<H, B, T, EmptyRouter>
|
||||
where
|
||||
H: Handler<B, T>,
|
||||
{
|
||||
on(MethodFilter::TRACE, handler)
|
||||
}
|
||||
|
||||
/// Route requests with the given method to the handler.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use axum::{
|
||||
/// routing::on,
|
||||
/// Router,
|
||||
/// routing::MethodFilter,
|
||||
/// };
|
||||
///
|
||||
/// async fn handler() {}
|
||||
///
|
||||
/// // Requests to `POST /` will go to `handler`.
|
||||
/// let app = Router::new().route("/", on(MethodFilter::POST, handler));
|
||||
/// # async {
|
||||
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
/// # };
|
||||
/// ```
|
||||
pub fn on<H, B, T>(method: MethodFilter, handler: H) -> MethodRouter<H, B, T, EmptyRouter>
|
||||
where
|
||||
H: Handler<B, T>,
|
||||
{
|
||||
MethodRouter {
|
||||
method,
|
||||
handler,
|
||||
fallback: EmptyRouter::method_not_allowed(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// A handler [`Service`] that accepts requests based on a [`MethodFilter`] and
|
||||
/// allows chaining additional handlers.
|
||||
pub struct MethodRouter<H, B, T, F> {
|
||||
pub(crate) method: MethodFilter,
|
||||
pub(crate) handler: H,
|
||||
pub(crate) fallback: F,
|
||||
pub(crate) _marker: PhantomData<fn() -> (B, T)>,
|
||||
}
|
||||
|
||||
impl<H, B, T, F> fmt::Debug for MethodRouter<H, B, T, F>
|
||||
where
|
||||
T: fmt::Debug,
|
||||
F: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("MethodRouter")
|
||||
.field("method", &self.method)
|
||||
.field("handler", &format_args!("{}", std::any::type_name::<H>()))
|
||||
.field("fallback", &self.fallback)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, B, T, F> Clone for MethodRouter<H, B, T, F>
|
||||
where
|
||||
H: Clone,
|
||||
F: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
method: self.method,
|
||||
handler: self.handler.clone(),
|
||||
fallback: self.fallback.clone(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, B, T, F> Copy for MethodRouter<H, B, T, F>
|
||||
where
|
||||
H: Copy,
|
||||
F: Copy,
|
||||
{
|
||||
}
|
||||
|
||||
impl<H, B, T, F> MethodRouter<H, B, T, F> {
|
||||
/// Chain an additional handler that will accept all requests regardless of
|
||||
/// its HTTP method.
|
||||
///
|
||||
/// See [`MethodRouter::get`] for an example.
|
||||
pub fn any<H2, T2>(self, handler: H2) -> MethodRouter<H2, B, T2, Self>
|
||||
where
|
||||
H2: Handler<B, T2>,
|
||||
{
|
||||
self.on(MethodFilter::all(), handler)
|
||||
}
|
||||
|
||||
/// Chain an additional handler that will only accept `CONNECT` requests.
|
||||
///
|
||||
/// See [`MethodRouter::get`] for an example.
|
||||
pub fn connect<H2, T2>(self, handler: H2) -> MethodRouter<H2, B, T2, Self>
|
||||
where
|
||||
H2: Handler<B, T2>,
|
||||
{
|
||||
self.on(MethodFilter::CONNECT, handler)
|
||||
}
|
||||
|
||||
/// Chain an additional handler that will only accept `DELETE` requests.
|
||||
///
|
||||
/// See [`MethodRouter::get`] for an example.
|
||||
pub fn delete<H2, T2>(self, handler: H2) -> MethodRouter<H2, B, T2, Self>
|
||||
where
|
||||
H2: Handler<B, T2>,
|
||||
{
|
||||
self.on(MethodFilter::DELETE, handler)
|
||||
}
|
||||
|
||||
/// Chain an additional handler that will only accept `GET` requests.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use axum::{routing::post, Router};
|
||||
///
|
||||
/// async fn handler() {}
|
||||
///
|
||||
/// async fn other_handler() {}
|
||||
///
|
||||
/// // Requests to `GET /` will go to `handler` and `POST /` will go to
|
||||
/// // `other_handler`.
|
||||
/// let app = Router::new().route("/", post(handler).get(other_handler));
|
||||
/// # async {
|
||||
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
/// # };
|
||||
/// ```
|
||||
///
|
||||
/// Note that `get` routes will also be called for `HEAD` requests but will have
|
||||
/// the response body removed. Make sure to add explicit `HEAD` routes
|
||||
/// afterwards.
|
||||
pub fn get<H2, T2>(self, handler: H2) -> MethodRouter<H2, B, T2, Self>
|
||||
where
|
||||
H2: Handler<B, T2>,
|
||||
{
|
||||
self.on(MethodFilter::GET | MethodFilter::HEAD, handler)
|
||||
}
|
||||
|
||||
/// Chain an additional handler that will only accept `HEAD` requests.
|
||||
///
|
||||
/// See [`MethodRouter::get`] for an example.
|
||||
pub fn head<H2, T2>(self, handler: H2) -> MethodRouter<H2, B, T2, Self>
|
||||
where
|
||||
H2: Handler<B, T2>,
|
||||
{
|
||||
self.on(MethodFilter::HEAD, handler)
|
||||
}
|
||||
|
||||
/// Chain an additional handler that will only accept `OPTIONS` requests.
|
||||
///
|
||||
/// See [`MethodRouter::get`] for an example.
|
||||
pub fn options<H2, T2>(self, handler: H2) -> MethodRouter<H2, B, T2, Self>
|
||||
where
|
||||
H2: Handler<B, T2>,
|
||||
{
|
||||
self.on(MethodFilter::OPTIONS, handler)
|
||||
}
|
||||
|
||||
/// Chain an additional handler that will only accept `PATCH` requests.
|
||||
///
|
||||
/// See [`MethodRouter::get`] for an example.
|
||||
pub fn patch<H2, T2>(self, handler: H2) -> MethodRouter<H2, B, T2, Self>
|
||||
where
|
||||
H2: Handler<B, T2>,
|
||||
{
|
||||
self.on(MethodFilter::PATCH, handler)
|
||||
}
|
||||
|
||||
/// Chain an additional handler that will only accept `POST` requests.
|
||||
///
|
||||
/// See [`MethodRouter::get`] for an example.
|
||||
pub fn post<H2, T2>(self, handler: H2) -> MethodRouter<H2, B, T2, Self>
|
||||
where
|
||||
H2: Handler<B, T2>,
|
||||
{
|
||||
self.on(MethodFilter::POST, handler)
|
||||
}
|
||||
|
||||
/// Chain an additional handler that will only accept `PUT` requests.
|
||||
///
|
||||
/// See [`MethodRouter::get`] for an example.
|
||||
pub fn put<H2, T2>(self, handler: H2) -> MethodRouter<H2, B, T2, Self>
|
||||
where
|
||||
H2: Handler<B, T2>,
|
||||
{
|
||||
self.on(MethodFilter::PUT, handler)
|
||||
}
|
||||
|
||||
/// Chain an additional handler that will only accept `TRACE` requests.
|
||||
///
|
||||
/// See [`MethodRouter::get`] for an example.
|
||||
pub fn trace<H2, T2>(self, handler: H2) -> MethodRouter<H2, B, T2, Self>
|
||||
where
|
||||
H2: Handler<B, T2>,
|
||||
{
|
||||
self.on(MethodFilter::TRACE, handler)
|
||||
}
|
||||
|
||||
/// Chain an additional handler that will accept requests matching the given
|
||||
/// `MethodFilter`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use axum::{
|
||||
/// routing::get,
|
||||
/// Router,
|
||||
/// routing::MethodFilter
|
||||
/// };
|
||||
///
|
||||
/// async fn handler() {}
|
||||
///
|
||||
/// async fn other_handler() {}
|
||||
///
|
||||
/// // Requests to `GET /` will go to `handler` and `DELETE /` will go to
|
||||
/// // `other_handler`
|
||||
/// let app = Router::new().route("/", get(handler).on(MethodFilter::DELETE, other_handler));
|
||||
/// # async {
|
||||
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
/// # };
|
||||
/// ```
|
||||
pub fn on<H2, T2>(self, method: MethodFilter, handler: H2) -> MethodRouter<H2, B, T2, Self>
|
||||
where
|
||||
H2: Handler<B, T2>,
|
||||
{
|
||||
MethodRouter {
|
||||
method,
|
||||
handler,
|
||||
fallback: self,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, B, T, F> Service<Request<B>> for MethodRouter<H, B, T, F>
|
||||
where
|
||||
H: Handler<B, T>,
|
||||
F: Service<Request<B>, Response = Response<BoxBody>, Error = Infallible> + Clone,
|
||||
B: Send + 'static,
|
||||
{
|
||||
type Response = Response<BoxBody>;
|
||||
type Error = Infallible;
|
||||
type Future = MethodRouterFuture<F, B>;
|
||||
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request<B>) -> Self::Future {
|
||||
let req_method = req.method().clone();
|
||||
|
||||
let fut = if self.method.matches(req.method()) {
|
||||
let fut = Handler::call(self.handler.clone(), req);
|
||||
Either::A { inner: fut }
|
||||
} else {
|
||||
let fut = self.fallback.clone().oneshot(req);
|
||||
Either::B { inner: fut }
|
||||
};
|
||||
|
||||
MethodRouterFuture {
|
||||
inner: fut,
|
||||
req_method,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pin_project! {
|
||||
/// The response future for [`MethodRouter`].
|
||||
pub struct MethodRouterFuture<F, B>
|
||||
where
|
||||
F: Service<Request<B>>
|
||||
{
|
||||
#[pin]
|
||||
pub(super) inner: Either<
|
||||
BoxFuture<'static, Response<BoxBody>>,
|
||||
Oneshot<F, Request<B>>,
|
||||
>,
|
||||
pub(super) req_method: Method,
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, B> Future for MethodRouterFuture<F, B>
|
||||
where
|
||||
F: Service<Request<B>, Response = Response<BoxBody>>,
|
||||
{
|
||||
type Output = Result<Response<BoxBody>, F::Error>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = self.project();
|
||||
let response = match this.inner.project() {
|
||||
EitherProj::A { inner } => ready!(inner.poll(cx)),
|
||||
EitherProj::B { inner } => ready!(inner.poll(cx))?,
|
||||
};
|
||||
|
||||
if this.req_method == &Method::HEAD {
|
||||
let response = response.map(|_| box_body(Empty::new()));
|
||||
Poll::Ready(Ok(response))
|
||||
} else {
|
||||
Poll::Ready(Ok(response))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, B> fmt::Debug for MethodRouterFuture<F, B>
|
||||
where
|
||||
F: Service<Request<B>>,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("MethodRouterFuture").finish()
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
//! Routing between [`Service`]s.
|
||||
//! Routing between [`Service`]s and handlers.
|
||||
|
||||
use self::future::{EmptyRouterFuture, NestedFuture, RouteFuture, RoutesFuture};
|
||||
use crate::{
|
||||
|
@ -28,12 +28,19 @@ use tower_layer::Layer;
|
|||
use tower_service::Service;
|
||||
|
||||
pub mod future;
|
||||
pub mod handler_method_router;
|
||||
pub mod service_method_router;
|
||||
|
||||
mod method_filter;
|
||||
mod or;
|
||||
|
||||
pub use self::method_filter::MethodFilter;
|
||||
|
||||
#[doc(no_inline)]
|
||||
pub use self::handler_method_router::{
|
||||
any, connect, delete, get, head, on, options, patch, post, put, trace, MethodRouter,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
struct RouteId(u64);
|
||||
|
||||
|
@ -109,7 +116,7 @@ where
|
|||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use axum::{handler::{get, delete}, Router};
|
||||
/// use axum::{routing::{get, delete}, Router};
|
||||
///
|
||||
/// let app = Router::new()
|
||||
/// .route("/", get(root))
|
||||
|
@ -136,7 +143,7 @@ where
|
|||
/// Panics if the route overlaps with another route:
|
||||
///
|
||||
/// ```should_panic
|
||||
/// use axum::{handler::get, Router};
|
||||
/// use axum::{routing::get, Router};
|
||||
///
|
||||
/// let app = Router::new()
|
||||
/// .route("/", get(|| async {}))
|
||||
|
@ -149,7 +156,7 @@ where
|
|||
/// This also applies to `nest` which is similar to a wildcard route:
|
||||
///
|
||||
/// ```should_panic
|
||||
/// use axum::{handler::get, Router};
|
||||
/// use axum::{routing::get, Router};
|
||||
///
|
||||
/// let app = Router::new()
|
||||
/// // this is similar to `/api/*`
|
||||
|
@ -164,7 +171,7 @@ where
|
|||
/// Note that routes like `/:key` and `/foo` are considered overlapping:
|
||||
///
|
||||
/// ```should_panic
|
||||
/// use axum::{handler::get, Router};
|
||||
/// use axum::{routing::get, Router};
|
||||
///
|
||||
/// let app = Router::new()
|
||||
/// .route("/foo", get(|| async {}))
|
||||
|
@ -204,7 +211,7 @@ where
|
|||
///
|
||||
/// ```
|
||||
/// use axum::{
|
||||
/// handler::get,
|
||||
/// routing::get,
|
||||
/// Router,
|
||||
/// };
|
||||
/// use http::Uri;
|
||||
|
@ -239,7 +246,7 @@ where
|
|||
/// ```
|
||||
/// use axum::{
|
||||
/// extract::Path,
|
||||
/// handler::get,
|
||||
/// routing::get,
|
||||
/// Router,
|
||||
/// };
|
||||
/// use std::collections::HashMap;
|
||||
|
@ -265,7 +272,7 @@ where
|
|||
/// ```
|
||||
/// use axum::{
|
||||
/// Router,
|
||||
/// service::get,
|
||||
/// routing::service_method_router::get,
|
||||
/// error_handling::HandleErrorExt,
|
||||
/// http::StatusCode,
|
||||
/// };
|
||||
|
@ -294,7 +301,7 @@ where
|
|||
/// the prefix stripped.
|
||||
///
|
||||
/// ```rust
|
||||
/// use axum::{handler::get, http::Uri, Router};
|
||||
/// use axum::{routing::get, http::Uri, Router};
|
||||
///
|
||||
/// let app = Router::new()
|
||||
/// .route("/foo/*rest", get(|uri: Uri| async {
|
||||
|
@ -366,7 +373,7 @@ where
|
|||
///
|
||||
/// ```rust
|
||||
/// use axum::{
|
||||
/// handler::get,
|
||||
/// routing::get,
|
||||
/// Router,
|
||||
/// };
|
||||
/// use tower::limit::{ConcurrencyLimitLayer, ConcurrencyLimit};
|
||||
|
@ -395,7 +402,7 @@ where
|
|||
///
|
||||
/// ```rust
|
||||
/// use axum::{
|
||||
/// handler::get,
|
||||
/// routing::get,
|
||||
/// Router,
|
||||
/// };
|
||||
/// use tower_http::trace::TraceLayer;
|
||||
|
@ -440,7 +447,7 @@ where
|
|||
///
|
||||
/// ```
|
||||
/// use axum::{
|
||||
/// handler::get,
|
||||
/// routing::get,
|
||||
/// Router,
|
||||
/// };
|
||||
///
|
||||
|
@ -470,7 +477,7 @@ where
|
|||
/// ```
|
||||
/// use axum::{
|
||||
/// extract::ConnectInfo,
|
||||
/// handler::get,
|
||||
/// routing::get,
|
||||
/// Router,
|
||||
/// };
|
||||
/// use std::net::SocketAddr;
|
||||
|
@ -496,7 +503,7 @@ where
|
|||
/// ```
|
||||
/// use axum::{
|
||||
/// extract::connect_info::{ConnectInfo, Connected},
|
||||
/// handler::get,
|
||||
/// routing::get,
|
||||
/// Router,
|
||||
/// };
|
||||
/// use hyper::server::conn::AddrStream;
|
||||
|
@ -555,7 +562,7 @@ where
|
|||
///
|
||||
/// ```
|
||||
/// use axum::{
|
||||
/// handler::get,
|
||||
/// routing::get,
|
||||
/// Router,
|
||||
/// };
|
||||
/// #
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! Use Tower [`Service`]s to handle requests.
|
||||
//! Routing for [`Service`'s] based on HTTP methods.
|
||||
//!
|
||||
//! Most of the time applications will be written by composing
|
||||
//! [handlers](crate::handler), however sometimes you might have some general
|
||||
|
@ -13,10 +13,9 @@
|
|||
//! use tower_http::services::Redirect;
|
||||
//! use axum::{
|
||||
//! body::Body,
|
||||
//! handler::get,
|
||||
//! routing::{get, service_method_router as service},
|
||||
//! http::Request,
|
||||
//! Router,
|
||||
//! service,
|
||||
//! };
|
||||
//!
|
||||
//! async fn handler(request: Request<Body>) { /* ... */ }
|
||||
|
@ -66,7 +65,7 @@
|
|||
//!
|
||||
//! ```rust
|
||||
//! use axum::{
|
||||
//! handler::get,
|
||||
//! routing::get,
|
||||
//! Router,
|
||||
//! };
|
||||
//! use tower::ServiceBuilder;
|
||||
|
@ -95,30 +94,36 @@
|
|||
//!
|
||||
//! [`Redirect`]: tower_http::services::Redirect
|
||||
//! [load shed]: tower::load_shed
|
||||
//! [`Service`'s]: tower::Service
|
||||
|
||||
use crate::BoxError;
|
||||
use crate::{
|
||||
body::BoxBody,
|
||||
body::{box_body, BoxBody},
|
||||
routing::{EmptyRouter, MethodFilter},
|
||||
util::{Either, EitherProj},
|
||||
BoxError,
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use http::{Request, Response};
|
||||
use futures_util::ready;
|
||||
use http::{Method, Request, Response};
|
||||
use http_body::Empty;
|
||||
use pin_project_lite::pin_project;
|
||||
use std::marker::PhantomData;
|
||||
use std::{
|
||||
marker::PhantomData,
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use tower::util::Oneshot;
|
||||
use tower::ServiceExt as _;
|
||||
use tower_service::Service;
|
||||
|
||||
pub mod future;
|
||||
|
||||
/// Route requests with any standard HTTP method to the given service.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
///
|
||||
/// Note that this only accepts the standard HTTP methods. If you need to
|
||||
/// support non-standard methods you can route directly to a [`Service`].
|
||||
pub fn any<S, B>(svc: S) -> OnMethod<S, EmptyRouter<S::Error>, B>
|
||||
pub fn any<S, B>(svc: S) -> MethodRouter<S, EmptyRouter<S::Error>, B>
|
||||
where
|
||||
S: Service<Request<B>> + Clone,
|
||||
{
|
||||
|
@ -128,7 +133,7 @@ where
|
|||
/// Route `CONNECT` requests to the given service.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn connect<S, B>(svc: S) -> OnMethod<S, EmptyRouter<S::Error>, B>
|
||||
pub fn connect<S, B>(svc: S) -> MethodRouter<S, EmptyRouter<S::Error>, B>
|
||||
where
|
||||
S: Service<Request<B>> + Clone,
|
||||
{
|
||||
|
@ -138,7 +143,7 @@ where
|
|||
/// Route `DELETE` requests to the given service.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn delete<S, B>(svc: S) -> OnMethod<S, EmptyRouter<S::Error>, B>
|
||||
pub fn delete<S, B>(svc: S) -> MethodRouter<S, EmptyRouter<S::Error>, B>
|
||||
where
|
||||
S: Service<Request<B>> + Clone,
|
||||
{
|
||||
|
@ -153,7 +158,7 @@ where
|
|||
/// use axum::{
|
||||
/// http::Request,
|
||||
/// Router,
|
||||
/// service,
|
||||
/// routing::service_method_router as service,
|
||||
/// };
|
||||
/// use http::Response;
|
||||
/// use std::convert::Infallible;
|
||||
|
@ -173,7 +178,7 @@ where
|
|||
/// Note that `get` routes will also be called for `HEAD` requests but will have
|
||||
/// the response body removed. Make sure to add explicit `HEAD` routes
|
||||
/// afterwards.
|
||||
pub fn get<S, B>(svc: S) -> OnMethod<S, EmptyRouter<S::Error>, B>
|
||||
pub fn get<S, B>(svc: S) -> MethodRouter<S, EmptyRouter<S::Error>, B>
|
||||
where
|
||||
S: Service<Request<B>> + Clone,
|
||||
{
|
||||
|
@ -183,7 +188,7 @@ where
|
|||
/// Route `HEAD` requests to the given service.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn head<S, B>(svc: S) -> OnMethod<S, EmptyRouter<S::Error>, B>
|
||||
pub fn head<S, B>(svc: S) -> MethodRouter<S, EmptyRouter<S::Error>, B>
|
||||
where
|
||||
S: Service<Request<B>> + Clone,
|
||||
{
|
||||
|
@ -193,7 +198,7 @@ where
|
|||
/// Route `OPTIONS` requests to the given service.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn options<S, B>(svc: S) -> OnMethod<S, EmptyRouter<S::Error>, B>
|
||||
pub fn options<S, B>(svc: S) -> MethodRouter<S, EmptyRouter<S::Error>, B>
|
||||
where
|
||||
S: Service<Request<B>> + Clone,
|
||||
{
|
||||
|
@ -203,7 +208,7 @@ where
|
|||
/// Route `PATCH` requests to the given service.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn patch<S, B>(svc: S) -> OnMethod<S, EmptyRouter<S::Error>, B>
|
||||
pub fn patch<S, B>(svc: S) -> MethodRouter<S, EmptyRouter<S::Error>, B>
|
||||
where
|
||||
S: Service<Request<B>> + Clone,
|
||||
{
|
||||
|
@ -213,7 +218,7 @@ where
|
|||
/// Route `POST` requests to the given service.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn post<S, B>(svc: S) -> OnMethod<S, EmptyRouter<S::Error>, B>
|
||||
pub fn post<S, B>(svc: S) -> MethodRouter<S, EmptyRouter<S::Error>, B>
|
||||
where
|
||||
S: Service<Request<B>> + Clone,
|
||||
{
|
||||
|
@ -223,7 +228,7 @@ where
|
|||
/// Route `PUT` requests to the given service.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn put<S, B>(svc: S) -> OnMethod<S, EmptyRouter<S::Error>, B>
|
||||
pub fn put<S, B>(svc: S) -> MethodRouter<S, EmptyRouter<S::Error>, B>
|
||||
where
|
||||
S: Service<Request<B>> + Clone,
|
||||
{
|
||||
|
@ -233,7 +238,7 @@ where
|
|||
/// Route `TRACE` requests to the given service.
|
||||
///
|
||||
/// See [`get`] for an example.
|
||||
pub fn trace<S, B>(svc: S) -> OnMethod<S, EmptyRouter<S::Error>, B>
|
||||
pub fn trace<S, B>(svc: S) -> MethodRouter<S, EmptyRouter<S::Error>, B>
|
||||
where
|
||||
S: Service<Request<B>> + Clone,
|
||||
{
|
||||
|
@ -247,10 +252,9 @@ where
|
|||
/// ```rust
|
||||
/// use axum::{
|
||||
/// http::Request,
|
||||
/// handler::on,
|
||||
/// service,
|
||||
/// routing::on,
|
||||
/// Router,
|
||||
/// routing::MethodFilter,
|
||||
/// routing::{MethodFilter, service_method_router as service},
|
||||
/// };
|
||||
/// use http::Response;
|
||||
/// use std::convert::Infallible;
|
||||
|
@ -266,11 +270,11 @@ where
|
|||
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
/// # };
|
||||
/// ```
|
||||
pub fn on<S, B>(method: MethodFilter, svc: S) -> OnMethod<S, EmptyRouter<S::Error>, B>
|
||||
pub fn on<S, B>(method: MethodFilter, svc: S) -> MethodRouter<S, EmptyRouter<S::Error>, B>
|
||||
where
|
||||
S: Service<Request<B>> + Clone,
|
||||
{
|
||||
OnMethod {
|
||||
MethodRouter {
|
||||
method,
|
||||
svc,
|
||||
fallback: EmptyRouter::method_not_allowed(),
|
||||
|
@ -281,14 +285,14 @@ where
|
|||
/// A [`Service`] that accepts requests based on a [`MethodFilter`] and allows
|
||||
/// chaining additional services.
|
||||
#[derive(Debug)] // TODO(david): don't require debug for B
|
||||
pub struct OnMethod<S, F, B> {
|
||||
pub struct MethodRouter<S, F, B> {
|
||||
pub(crate) method: MethodFilter,
|
||||
pub(crate) svc: S,
|
||||
pub(crate) fallback: F,
|
||||
pub(crate) _request_body: PhantomData<fn() -> B>,
|
||||
}
|
||||
|
||||
impl<S, F, B> Clone for OnMethod<S, F, B>
|
||||
impl<S, F, B> Clone for MethodRouter<S, F, B>
|
||||
where
|
||||
S: Clone,
|
||||
F: Clone,
|
||||
|
@ -303,12 +307,12 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<S, F, B> OnMethod<S, F, B> {
|
||||
impl<S, F, B> MethodRouter<S, F, B> {
|
||||
/// Chain an additional service that will accept all requests regardless of
|
||||
/// its HTTP method.
|
||||
///
|
||||
/// See [`OnMethod::get`] for an example.
|
||||
pub fn any<T>(self, svc: T) -> OnMethod<T, Self, B>
|
||||
/// See [`MethodRouter::get`] for an example.
|
||||
pub fn any<T>(self, svc: T) -> MethodRouter<T, Self, B>
|
||||
where
|
||||
T: Service<Request<B>> + Clone,
|
||||
{
|
||||
|
@ -317,8 +321,8 @@ impl<S, F, B> OnMethod<S, F, B> {
|
|||
|
||||
/// Chain an additional service that will only accept `CONNECT` requests.
|
||||
///
|
||||
/// See [`OnMethod::get`] for an example.
|
||||
pub fn connect<T>(self, svc: T) -> OnMethod<T, Self, B>
|
||||
/// See [`MethodRouter::get`] for an example.
|
||||
pub fn connect<T>(self, svc: T) -> MethodRouter<T, Self, B>
|
||||
where
|
||||
T: Service<Request<B>> + Clone,
|
||||
{
|
||||
|
@ -327,8 +331,8 @@ impl<S, F, B> OnMethod<S, F, B> {
|
|||
|
||||
/// Chain an additional service that will only accept `DELETE` requests.
|
||||
///
|
||||
/// See [`OnMethod::get`] for an example.
|
||||
pub fn delete<T>(self, svc: T) -> OnMethod<T, Self, B>
|
||||
/// See [`MethodRouter::get`] for an example.
|
||||
pub fn delete<T>(self, svc: T) -> MethodRouter<T, Self, B>
|
||||
where
|
||||
T: Service<Request<B>> + Clone,
|
||||
{
|
||||
|
@ -342,10 +346,8 @@ impl<S, F, B> OnMethod<S, F, B> {
|
|||
/// ```rust
|
||||
/// use axum::{
|
||||
/// http::Request,
|
||||
/// handler::on,
|
||||
/// service,
|
||||
/// Router,
|
||||
/// routing::MethodFilter,
|
||||
/// routing::{MethodFilter, on, service_method_router as service},
|
||||
/// };
|
||||
/// use http::Response;
|
||||
/// use std::convert::Infallible;
|
||||
|
@ -370,7 +372,7 @@ impl<S, F, B> OnMethod<S, F, B> {
|
|||
/// Note that `get` routes will also be called for `HEAD` requests but will have
|
||||
/// the response body removed. Make sure to add explicit `HEAD` routes
|
||||
/// afterwards.
|
||||
pub fn get<T>(self, svc: T) -> OnMethod<T, Self, B>
|
||||
pub fn get<T>(self, svc: T) -> MethodRouter<T, Self, B>
|
||||
where
|
||||
T: Service<Request<B>> + Clone,
|
||||
{
|
||||
|
@ -379,8 +381,8 @@ impl<S, F, B> OnMethod<S, F, B> {
|
|||
|
||||
/// Chain an additional service that will only accept `HEAD` requests.
|
||||
///
|
||||
/// See [`OnMethod::get`] for an example.
|
||||
pub fn head<T>(self, svc: T) -> OnMethod<T, Self, B>
|
||||
/// See [`MethodRouter::get`] for an example.
|
||||
pub fn head<T>(self, svc: T) -> MethodRouter<T, Self, B>
|
||||
where
|
||||
T: Service<Request<B>> + Clone,
|
||||
{
|
||||
|
@ -389,8 +391,8 @@ impl<S, F, B> OnMethod<S, F, B> {
|
|||
|
||||
/// Chain an additional service that will only accept `OPTIONS` requests.
|
||||
///
|
||||
/// See [`OnMethod::get`] for an example.
|
||||
pub fn options<T>(self, svc: T) -> OnMethod<T, Self, B>
|
||||
/// See [`MethodRouter::get`] for an example.
|
||||
pub fn options<T>(self, svc: T) -> MethodRouter<T, Self, B>
|
||||
where
|
||||
T: Service<Request<B>> + Clone,
|
||||
{
|
||||
|
@ -399,8 +401,8 @@ impl<S, F, B> OnMethod<S, F, B> {
|
|||
|
||||
/// Chain an additional service that will only accept `PATCH` requests.
|
||||
///
|
||||
/// See [`OnMethod::get`] for an example.
|
||||
pub fn patch<T>(self, svc: T) -> OnMethod<T, Self, B>
|
||||
/// See [`MethodRouter::get`] for an example.
|
||||
pub fn patch<T>(self, svc: T) -> MethodRouter<T, Self, B>
|
||||
where
|
||||
T: Service<Request<B>> + Clone,
|
||||
{
|
||||
|
@ -409,8 +411,8 @@ impl<S, F, B> OnMethod<S, F, B> {
|
|||
|
||||
/// Chain an additional service that will only accept `POST` requests.
|
||||
///
|
||||
/// See [`OnMethod::get`] for an example.
|
||||
pub fn post<T>(self, svc: T) -> OnMethod<T, Self, B>
|
||||
/// See [`MethodRouter::get`] for an example.
|
||||
pub fn post<T>(self, svc: T) -> MethodRouter<T, Self, B>
|
||||
where
|
||||
T: Service<Request<B>> + Clone,
|
||||
{
|
||||
|
@ -419,8 +421,8 @@ impl<S, F, B> OnMethod<S, F, B> {
|
|||
|
||||
/// Chain an additional service that will only accept `PUT` requests.
|
||||
///
|
||||
/// See [`OnMethod::get`] for an example.
|
||||
pub fn put<T>(self, svc: T) -> OnMethod<T, Self, B>
|
||||
/// See [`MethodRouter::get`] for an example.
|
||||
pub fn put<T>(self, svc: T) -> MethodRouter<T, Self, B>
|
||||
where
|
||||
T: Service<Request<B>> + Clone,
|
||||
{
|
||||
|
@ -429,8 +431,8 @@ impl<S, F, B> OnMethod<S, F, B> {
|
|||
|
||||
/// Chain an additional service that will only accept `TRACE` requests.
|
||||
///
|
||||
/// See [`OnMethod::get`] for an example.
|
||||
pub fn trace<T>(self, svc: T) -> OnMethod<T, Self, B>
|
||||
/// See [`MethodRouter::get`] for an example.
|
||||
pub fn trace<T>(self, svc: T) -> MethodRouter<T, Self, B>
|
||||
where
|
||||
T: Service<Request<B>> + Clone,
|
||||
{
|
||||
|
@ -445,10 +447,8 @@ impl<S, F, B> OnMethod<S, F, B> {
|
|||
/// ```rust
|
||||
/// use axum::{
|
||||
/// http::Request,
|
||||
/// handler::on,
|
||||
/// service,
|
||||
/// Router,
|
||||
/// routing::MethodFilter,
|
||||
/// routing::{MethodFilter, on, service_method_router as service},
|
||||
/// };
|
||||
/// use http::Response;
|
||||
/// use std::convert::Infallible;
|
||||
|
@ -468,11 +468,11 @@ impl<S, F, B> OnMethod<S, F, B> {
|
|||
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
/// # };
|
||||
/// ```
|
||||
pub fn on<T>(self, method: MethodFilter, svc: T) -> OnMethod<T, Self, B>
|
||||
pub fn on<T>(self, method: MethodFilter, svc: T) -> MethodRouter<T, Self, B>
|
||||
where
|
||||
T: Service<Request<B>> + Clone,
|
||||
{
|
||||
OnMethod {
|
||||
MethodRouter {
|
||||
method,
|
||||
svc,
|
||||
fallback: self,
|
||||
|
@ -481,9 +481,7 @@ impl<S, F, B> OnMethod<S, F, B> {
|
|||
}
|
||||
}
|
||||
|
||||
// this is identical to `routing::OnMethod`'s implementation. Would be nice to find a way to clean
|
||||
// that up, but not sure its possible.
|
||||
impl<S, F, B, ResBody> Service<Request<B>> for OnMethod<S, F, B>
|
||||
impl<S, F, B, ResBody> Service<Request<B>> for MethodRouter<S, F, B>
|
||||
where
|
||||
S: Service<Request<B>, Response = Response<ResBody>> + Clone,
|
||||
ResBody: http_body::Body<Data = Bytes> + Send + Sync + 'static,
|
||||
|
@ -492,15 +490,13 @@ where
|
|||
{
|
||||
type Response = Response<BoxBody>;
|
||||
type Error = S::Error;
|
||||
type Future = future::OnMethodFuture<S, F, B>;
|
||||
type Future = MethodRouterFuture<S, F, B>;
|
||||
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request<B>) -> Self::Future {
|
||||
use crate::util::Either;
|
||||
|
||||
let req_method = req.method().clone();
|
||||
|
||||
let f = if self.method.matches(req.method()) {
|
||||
|
@ -511,17 +507,59 @@ where
|
|||
Either::B { inner: fut }
|
||||
};
|
||||
|
||||
future::OnMethodFuture {
|
||||
MethodRouterFuture {
|
||||
inner: f,
|
||||
req_method,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pin_project! {
|
||||
/// The response future for [`MethodRouter`].
|
||||
pub struct MethodRouterFuture<S, F, B>
|
||||
where
|
||||
S: Service<Request<B>>,
|
||||
F: Service<Request<B>>
|
||||
{
|
||||
#[pin]
|
||||
pub(super) inner: Either<
|
||||
Oneshot<S, Request<B>>,
|
||||
Oneshot<F, Request<B>>,
|
||||
>,
|
||||
pub(super) req_method: Method,
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, F, B, ResBody> Future for MethodRouterFuture<S, F, B>
|
||||
where
|
||||
S: Service<Request<B>, Response = Response<ResBody>> + Clone,
|
||||
ResBody: http_body::Body<Data = Bytes> + Send + Sync + 'static,
|
||||
ResBody::Error: Into<BoxError>,
|
||||
F: Service<Request<B>, Response = Response<BoxBody>, Error = S::Error>,
|
||||
{
|
||||
type Output = Result<Response<BoxBody>, S::Error>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = self.project();
|
||||
|
||||
let response = match this.inner.project() {
|
||||
EitherProj::A { inner } => ready!(inner.poll(cx))?.map(box_body),
|
||||
EitherProj::B { inner } => ready!(inner.poll(cx))?,
|
||||
};
|
||||
|
||||
if this.req_method == &Method::HEAD {
|
||||
let response = response.map(|_| box_body(Empty::new()));
|
||||
Poll::Ready(Ok(response))
|
||||
} else {
|
||||
Poll::Ready(Ok(response))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn traits() {
|
||||
use crate::tests::*;
|
||||
|
||||
assert_send::<OnMethod<(), (), NotSendSync>>();
|
||||
assert_sync::<OnMethod<(), (), NotSendSync>>();
|
||||
assert_send::<MethodRouter<(), (), NotSendSync>>();
|
||||
assert_sync::<MethodRouter<(), (), NotSendSync>>();
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
//! [`Service`](tower::Service) future types.
|
||||
|
||||
use crate::{
|
||||
body::{box_body, BoxBody},
|
||||
util::{Either, EitherProj},
|
||||
BoxError,
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use futures_util::ready;
|
||||
use http::{Method, Request, Response};
|
||||
use http_body::Empty;
|
||||
use pin_project_lite::pin_project;
|
||||
use std::{
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use tower::util::Oneshot;
|
||||
use tower_service::Service;
|
||||
|
||||
pin_project! {
|
||||
/// The response future for [`OnMethod`](super::OnMethod).
|
||||
pub struct OnMethodFuture<S, F, B>
|
||||
where
|
||||
S: Service<Request<B>>,
|
||||
F: Service<Request<B>>
|
||||
{
|
||||
#[pin]
|
||||
pub(super) inner: Either<
|
||||
Oneshot<S, Request<B>>,
|
||||
Oneshot<F, Request<B>>,
|
||||
>,
|
||||
// pub(super) inner: crate::routing::future::RouteFuture<S, F, B>,
|
||||
pub(super) req_method: Method,
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, F, B, ResBody> Future for OnMethodFuture<S, F, B>
|
||||
where
|
||||
S: Service<Request<B>, Response = Response<ResBody>> + Clone,
|
||||
ResBody: http_body::Body<Data = Bytes> + Send + Sync + 'static,
|
||||
ResBody::Error: Into<BoxError>,
|
||||
F: Service<Request<B>, Response = Response<BoxBody>, Error = S::Error>,
|
||||
{
|
||||
type Output = Result<Response<BoxBody>, S::Error>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = self.project();
|
||||
|
||||
let response = match this.inner.project() {
|
||||
EitherProj::A { inner } => ready!(inner.poll(cx))?.map(box_body),
|
||||
EitherProj::B { inner } => ready!(inner.poll(cx))?,
|
||||
};
|
||||
|
||||
if this.req_method == &Method::HEAD {
|
||||
let response = response.map(|_| box_body(Empty::new()));
|
||||
Poll::Ready(Ok(response))
|
||||
} else {
|
||||
Poll::Ready(Ok(response))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -38,7 +38,7 @@ mod for_handlers {
|
|||
|
||||
mod for_services {
|
||||
use super::*;
|
||||
use crate::service::get;
|
||||
use crate::routing::service_method_router::get;
|
||||
use http::header::HeaderValue;
|
||||
|
||||
#[tokio::test]
|
||||
|
|
|
@ -4,10 +4,10 @@ use crate::error_handling::HandleErrorLayer;
|
|||
use crate::BoxError;
|
||||
use crate::{
|
||||
extract::{self, Path},
|
||||
handler::{any, delete, get, on, patch, post, Handler},
|
||||
handler::Handler,
|
||||
response::IntoResponse,
|
||||
routing::MethodFilter,
|
||||
service, Json, Router,
|
||||
routing::{any, delete, get, on, patch, post, service_method_router as service, MethodFilter},
|
||||
Json, Router,
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use http::{
|
||||
|
|
|
@ -196,16 +196,18 @@ async fn many_ors() {
|
|||
|
||||
#[tokio::test]
|
||||
async fn services() {
|
||||
use crate::routing::service_method_router::get;
|
||||
|
||||
let app = Router::new()
|
||||
.route(
|
||||
"/foo",
|
||||
crate::service::get(service_fn(|_: Request<Body>| async {
|
||||
get(service_fn(|_: Request<Body>| async {
|
||||
Ok::<_, Infallible>(Response::new(Body::empty()))
|
||||
})),
|
||||
)
|
||||
.or(Router::new().route(
|
||||
"/bar",
|
||||
crate::service::get(service_fn(|_: Request<Body>| async {
|
||||
get(service_fn(|_: Request<Body>| async {
|
||||
Ok::<_, Infallible>(Response::new(Body::empty()))
|
||||
})),
|
||||
));
|
||||
|
|
Loading…
Add table
Reference in a new issue