Replace route with Router::new().route() (#215)

This way there is now only one way to create a router:

```rust
use axum::{Router, handler::get};

let app = Router::new()
    .route("/foo", get(handler))
    .route("/foo", get(handler));
```

`nest` was changed in the same way:

```rust
use axum::Router;

let app = Router::new().nest("/foo", service);
```
This commit is contained in:
David Pedersen 2021-08-19 22:37:48 +02:00 committed by GitHub
parent 97b53768ba
commit ca4d9a2bb9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
49 changed files with 652 additions and 625 deletions

View file

@ -9,8 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Overall compile time improvements. If you're having issues with compile time
please file an issue!
- Remove `prelude`. Explicit imports are now required.
- Add dedicated `Router` to replace the `RoutingDsl` trait
- Remove `prelude`. Explicit imports are now required ([#195](https://github.com/tokio-rs/axum/pull/195))
- Add dedicated `Router` to replace the `RoutingDsl` trait ([#214](https://github.com/tokio-rs/axum/pull/214))
- Replace `axum::route(...)` with `axum::Router::new().route(...)`. This means
there is now only one way to create a new router. Same goes for
`axum::routing::nest`. ([#215](https://github.com/tokio-rs/axum/pull/215))
- Make `FromRequest` default to being generic over `body::Body` ([#146](https://github.com/tokio-rs/axum/pull/146))
- Implement `std::error::Error` for all rejections ([#153](https://github.com/tokio-rs/axum/pull/153))
- Add `Router::or` for combining routes ([#108](https://github.com/tokio-rs/axum/pull/108))

View file

@ -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, route, AddExtensionLayer, Json};
use axum::{extract::Extension, handler::get, response::Html, AddExtensionLayer, Json, Router};
use starwars::{QueryRoot, StarWars, StarWarsSchema};
async fn graphql_handler(schema: Extension<StarWarsSchema>, req: Json<Request>) -> Json<Response> {
@ -20,7 +20,8 @@ async fn main() {
.data(StarWars::new())
.finish();
let app = route("/", get(graphql_playground).post(graphql_handler))
let app = Router::new()
.route("/", get(graphql_playground).post(graphql_handler))
.layer(AddExtensionLayer::new(schema));
println!("Playground: http://localhost:3000");

View file

@ -10,8 +10,8 @@ use axum::extract::ws::{Message, WebSocket, WebSocketUpgrade};
use axum::extract::Extension;
use axum::handler::get;
use axum::response::{Html, IntoResponse};
use axum::route;
use axum::AddExtensionLayer;
use axum::Router;
use futures::{sink::SinkExt, stream::StreamExt};
use std::collections::HashSet;
use std::net::SocketAddr;
@ -31,7 +31,8 @@ async fn main() {
let app_state = Arc::new(AppState { user_set, tx });
let app = route("/", get(index))
let app = Router::new()
.route("/", get(index))
.route("/websocket", get(websocket_handler))
.layer(AddExtensionLayer::new(app_state));

View file

@ -14,7 +14,7 @@ use axum::{
handler::{get, post},
http::{Response, StatusCode},
response::IntoResponse,
route, AddExtensionLayer, Json,
AddExtensionLayer, Json, Router,
};
use serde::{Deserialize, Serialize};
use serde_json::json;
@ -37,7 +37,8 @@ async fn main() {
let user_repo = Arc::new(ExampleUserRepo) as DynUserRepo;
// Build our application with some routes
let app = route("/users/:id", get(users_show))
let app = Router::new()
.route("/users/:id", get(users_show))
.route("/users", post(users_create))
// Add our `user_repo` to all request's extensions so handlers can access
// it.

View file

@ -4,7 +4,7 @@
//! cargo run -p example-form
//! ```
use axum::{extract::Form, handler::get, response::Html, route};
use axum::{extract::Form, handler::get, response::Html, Router};
use serde::Deserialize;
use std::net::SocketAddr;
@ -17,7 +17,7 @@ async fn main() {
tracing_subscriber::fmt::init();
// build our application with some routes
let app = route("/", get(show_form).post(accept_form));
let app = Router::new().route("/", get(show_form).post(accept_form));
// run it with hyper
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));

View file

@ -9,7 +9,7 @@ use axum::{
handler::get,
http::{Response, StatusCode},
response::Html,
route,
Router,
};
use std::net::SocketAddr;
use tower::util::MapResponseLayer;
@ -23,7 +23,8 @@ async fn main() {
tracing_subscriber::fmt::init();
// build our application with a route
let app = route("/", get(handler))
let app = Router::new()
.route("/", get(handler))
// make sure this is added as the very last thing
.layer(MapResponseLayer::new(map_404));

View file

@ -4,13 +4,13 @@
//! cargo run -p example-hello-world
//! ```
use axum::{handler::get, route};
use axum::{handler::get, Router};
use std::net::SocketAddr;
#[tokio::main]
async fn main() {
// build our application with a route
let app = route("/foo", get(handler));
let app = Router::new().route("/", get(handler));
// run it
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));

View file

@ -12,8 +12,8 @@ use axum::{
handler::{delete, get, Handler},
http::StatusCode,
response::IntoResponse,
route,
routing::{BoxRoute, Router},
routing::BoxRoute,
Router,
};
use std::{
borrow::Cow,
@ -38,29 +38,30 @@ async fn main() {
tracing_subscriber::fmt::init();
// Build our application by composing routes
let app = route(
"/:key",
// Add compression to `kv_get`
get(kv_get.layer(CompressionLayer::new()))
// But don't compress `kv_set`
.post(kv_set),
)
.route("/keys", get(list_keys))
// Nest our admin routes under `/admin`
.nest("/admin", admin_routes())
// Add middleware to all routes
.layer(
ServiceBuilder::new()
.load_shed()
.concurrency_limit(1024)
.timeout(Duration::from_secs(10))
.layer(TraceLayer::new_for_http())
.layer(AddExtensionLayer::new(SharedState::default()))
.into_inner(),
)
// Handle errors from middleware
.handle_error(handle_error)
.check_infallible();
let app = Router::new()
.route(
"/:key",
// Add compression to `kv_get`
get(kv_get.layer(CompressionLayer::new()))
// But don't compress `kv_set`
.post(kv_set),
)
.route("/keys", get(list_keys))
// Nest our admin routes under `/admin`
.nest("/admin", admin_routes())
// Add middleware to all routes
.layer(
ServiceBuilder::new()
.load_shed()
.concurrency_limit(1024)
.timeout(Duration::from_secs(10))
.layer(TraceLayer::new_for_http())
.layer(AddExtensionLayer::new(SharedState::default()))
.into_inner(),
)
// Handle errors from middleware
.handle_error(handle_error)
.check_infallible();
// Run our app with hyper
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
@ -117,7 +118,8 @@ fn admin_routes() -> Router<BoxRoute> {
state.write().unwrap().db.remove(&key);
}
route("/keys", delete(delete_all_keys))
Router::new()
.route("/keys", delete(delete_all_keys))
.route("/key/:key", delete(remove_key))
// Require bearer auth for all admin routes
.layer(RequireAuthorizationLayer::bearer("secret-token"))

View file

@ -8,7 +8,7 @@ use axum::{
extract::{ContentLengthLimit, Multipart},
handler::get,
response::Html,
route,
Router,
};
use std::net::SocketAddr;
@ -21,7 +21,8 @@ async fn main() {
tracing_subscriber::fmt::init();
// build our application with some routes
let app = route("/", get(show_form).post(accept_form))
let app = Router::new()
.route("/", get(show_form).post(accept_form))
.layer(tower_http::trace::TraceLayer::new_for_http());
// run it with hyper

View file

@ -14,7 +14,7 @@ use axum::{
handler::get,
http::{header::SET_COOKIE, HeaderMap, Response},
response::{IntoResponse, Redirect},
route, AddExtensionLayer,
AddExtensionLayer, Router,
};
use oauth2::{
basic::BasicClient, reqwest::async_http_client, AuthUrl, AuthorizationCode, ClientId,
@ -42,8 +42,11 @@ async fn main() {
// `MemoryStore` just used as an example. Don't use this in production.
let store = MemoryStore::new();
let oauth_client = oauth_client();
let app = route("/", get(index))
let app = Router::new()
.route("/", get(index))
.route("/auth/discord", get(discord_auth))
.route("/auth/authorized", get(login_authorized))
.route("/protected", get(protected))

View file

@ -15,7 +15,7 @@ use axum::{
StatusCode,
},
response::IntoResponse,
route, AddExtensionLayer,
AddExtensionLayer, Router,
};
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;
@ -32,7 +32,9 @@ async fn main() {
// `MemoryStore` just used as an example. Don't use this in production.
let store = MemoryStore::new();
let app = route("/", get(handler)).layer(AddExtensionLayer::new(store));
let app = Router::new()
.route("/", get(handler))
.layer(AddExtensionLayer::new(store));
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
tracing::debug!("listening on {}", addr);

View file

@ -9,7 +9,7 @@ use axum::{
handler::get,
http::StatusCode,
response::sse::{sse, Event, Sse},
routing::nest,
Router,
};
use futures::stream::{self, Stream};
use std::{convert::Infallible, net::SocketAddr, time::Duration};
@ -35,7 +35,8 @@ async fn main() {
});
// build our application with a route
let app = nest("/", static_files_service)
let app = Router::new()
.nest("/", static_files_service)
.route("/sse", get(sse_handler))
.layer(TraceLayer::new_for_http());

View file

@ -4,8 +4,8 @@
//! cargo run -p example-static-file-server
//! ```
use axum::{http::StatusCode, routing::nest};
use std::net::SocketAddr;
use axum::{http::StatusCode, service, Router};
use std::{convert::Infallible, net::SocketAddr};
use tower_http::{services::ServeDir, trace::TraceLayer};
#[tokio::main]
@ -19,16 +19,17 @@ async fn main() {
}
tracing_subscriber::fmt::init();
let app = nest(
"/static",
axum::service::get(ServeDir::new(".")).handle_error(|error: std::io::Error| {
Ok::<_, std::convert::Infallible>((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Unhandled internal error: {}", error),
))
}),
)
.layer(TraceLayer::new_for_http());
let app = Router::new()
.nest(
"/static",
service::get(ServeDir::new(".")).handle_error(|error: std::io::Error| {
Ok::<_, Infallible>((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Unhandled internal error: {}", error),
))
}),
)
.layer(TraceLayer::new_for_http());
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
tracing::debug!("listening on {}", addr);

View file

@ -11,7 +11,7 @@ use axum::{
handler::get,
http::{Response, StatusCode},
response::{Html, IntoResponse},
route,
Router,
};
use std::{convert::Infallible, net::SocketAddr};
@ -24,7 +24,7 @@ async fn main() {
tracing_subscriber::fmt::init();
// build our application with some routes
let app = route("/greet/:name", get(greet));
let app = Router::new().route("/greet/:name", get(greet));
// run it
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));

View file

@ -6,9 +6,8 @@
use axum::{
handler::{get, post},
route,
routing::{BoxRoute, Router},
Json,
routing::BoxRoute,
Json, Router,
};
use tower_http::trace::TraceLayer;
@ -34,7 +33,8 @@ async fn main() {
/// without having to create an HTTP server.
#[allow(dead_code)]
fn app() -> Router<BoxRoute> {
route("/", get(|| async { "Hello, World!" }))
Router::new()
.route("/", get(|| async { "Hello, World!" }))
.route(
"/json",
post(|payload: Json<serde_json::Value>| async move {

View file

@ -4,7 +4,7 @@
//! cargo run -p example-tls-rustls
//! ```
use axum::{handler::get, route};
use axum::{handler::get, Router};
use hyper::server::conn::Http;
use std::{fs::File, io::BufReader, sync::Arc};
use tokio::net::TcpListener;
@ -31,7 +31,7 @@ async fn main() {
let acceptor = TlsAcceptor::from(rustls_config);
let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap();
let app = route("/", get(handler));
let app = Router::new().route("/", get(handler));
loop {
let (stream, _addr) = listener.accept().await.unwrap();

View file

@ -18,7 +18,7 @@ use axum::{
handler::{get, patch},
http::StatusCode,
response::IntoResponse,
route, Json,
Json, Router,
};
use serde::{Deserialize, Serialize};
use std::{
@ -43,7 +43,8 @@ async fn main() {
let db = Db::default();
// Compose the routes
let app = route("/todos", get(todos_index).post(todos_create))
let app = Router::new()
.route("/todos", get(todos_index).post(todos_create))
.route("/todos/:id", patch(todos_update).delete(todos_delete))
// Add middleware to all routes
.layer(
@ -53,7 +54,6 @@ async fn main() {
.layer(AddExtensionLayer::new(db))
.into_inner(),
)
// If the timeout fails, map the error to a response
.handle_error(|error: BoxError| {
let result = if error.is::<tower::timeout::error::Elapsed>() {
Ok(StatusCode::REQUEST_TIMEOUT)

View file

@ -9,7 +9,7 @@ use axum::{
extract::{Extension, FromRequest, RequestParts},
handler::get,
http::StatusCode,
route, AddExtensionLayer,
AddExtensionLayer, Router,
};
use bb8::{Pool, PooledConnection};
use bb8_postgres::PostgresConnectionManager;
@ -31,11 +31,12 @@ async fn main() {
let pool = Pool::builder().build(manager).await.unwrap();
// build our application with some routes
let app = route(
"/",
get(using_connection_pool_extractor).post(using_connection_extractor),
)
.layer(AddExtensionLayer::new(pool));
let app = Router::new()
.route(
"/",
get(using_connection_pool_extractor).post(using_connection_extractor),
)
.layer(AddExtensionLayer::new(pool));
// run it with hyper
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));

View file

@ -4,7 +4,7 @@
//! cargo run -p example-tracing-aka-logging
//! ```
use axum::{handler::get, response::Html, route};
use axum::{handler::get, response::Html, Router};
use std::net::SocketAddr;
use tower_http::trace::TraceLayer;
@ -20,7 +20,8 @@ async fn main() {
tracing_subscriber::fmt::init();
// build our application with a route
let app = route("/", get(handler))
let app = Router::new()
.route("/", get(handler))
// `TraceLayer` is provided by tower-http so you have to add that as a dependency.
// It provides good defaults but is also very customizable.
// See https://docs.rs/tower-http/0.1.1/tower_http/trace/index.html for more details.

View file

@ -9,7 +9,7 @@ use axum::{
extract::connect_info::{self, ConnectInfo},
handler::get,
http::{Method, Request, StatusCode, Uri},
route,
Router,
};
use futures::ready;
use hyper::{
@ -53,7 +53,7 @@ async fn main() {
let uds = UnixListener::bind(path.clone()).unwrap();
tokio::spawn(async {
let app = route("/", get(handler));
let app = Router::new().route("/", get(handler));
axum::Server::builder(ServerAccept { uds })
.serve(app.into_make_service_with_connect_info::<UdsConnectInfo, _>())

View file

@ -11,7 +11,7 @@ use axum::{
handler::get,
http::{Response, StatusCode},
response::IntoResponse,
route,
Router,
};
use std::collections::HashMap;
use std::net::SocketAddr;
@ -25,7 +25,7 @@ async fn main() {
tracing_subscriber::fmt::init();
// build our application with some routes
let app = route("/:version/foo", get(handler));
let app = Router::new().route("/:version/foo", get(handler));
// run it
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));

View file

@ -14,7 +14,7 @@ use axum::{
handler::get,
http::StatusCode,
response::IntoResponse,
routing::nest,
Router,
};
use std::net::SocketAddr;
use tower_http::{
@ -31,25 +31,27 @@ async fn main() {
tracing_subscriber::fmt::init();
// build our application with some routes
let app = nest(
"/",
axum::service::get(
ServeDir::new("examples/websockets/assets").append_index_html_on_directories(true),
let app = Router::new()
.nest(
"/",
axum::service::get(
ServeDir::new("examples/websockets/assets").append_index_html_on_directories(true),
)
.handle_error(|error: std::io::Error| {
Ok::<_, std::convert::Infallible>((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Unhandled internal error: {}", error),
))
}),
)
.handle_error(|error: std::io::Error| {
Ok::<_, std::convert::Infallible>((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Unhandled internal error: {}", error),
))
}),
)
// routes are matched from bottom to top, so we have to put `nest` at the
// top since it matches all routes
.route("/ws", get(ws_handler))
// logging so we can see whats going on
.layer(
TraceLayer::new_for_http().make_span_with(DefaultMakeSpan::default().include_headers(true)),
);
// routes are matched from bottom to top, so we have to put `nest` at the
// top since it matches all routes
.route("/ws", get(ws_handler))
// logging so we can see whats going on
.layer(
TraceLayer::new_for_http()
.make_span_with(DefaultMakeSpan::default().include_headers(true)),
);
// run it with hyper
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));

View file

@ -131,7 +131,7 @@ where
mod tests {
use super::*;
use crate::Server;
use crate::{handler::get, route};
use crate::{handler::get, Router};
use std::net::{SocketAddr, TcpListener};
#[tokio::test]
@ -145,7 +145,7 @@ mod tests {
let (tx, rx) = tokio::sync::oneshot::channel();
tokio::spawn(async move {
let app = route("/", get(handler));
let app = Router::new().route("/", get(handler));
let server = Server::from_tcp(listener)
.unwrap()
.serve(app.into_make_service_with_connect_info::<SocketAddr, _>());
@ -187,7 +187,7 @@ mod tests {
let (tx, rx) = tokio::sync::oneshot::channel();
tokio::spawn(async move {
let app = route("/", get(handler));
let app = Router::new().route("/", get(handler));
let server = Server::from_tcp(listener)
.unwrap()
.serve(app.into_make_service_with_connect_info::<MyConnectInfo, _>());

View file

@ -12,14 +12,14 @@ use std::ops::Deref;
/// use axum::{
/// extract::ContentLengthLimit,
/// handler::post,
/// route,
/// Router,
/// };
///
/// async fn handler(body: ContentLengthLimit<String, 1024>) {
/// // ...
/// }
///
/// let app = route("/", post(handler));
/// let app = Router::new().route("/", post(handler));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };

View file

@ -13,7 +13,7 @@ use std::ops::Deref;
/// AddExtensionLayer,
/// extract::Extension,
/// handler::get,
/// route,
/// Router,
/// };
/// use std::sync::Arc;
///
@ -28,7 +28,7 @@ use std::ops::Deref;
///
/// let state = Arc::new(State { /* ... */ });
///
/// let app = route("/", get(handler))
/// let app = Router::new().route("/", get(handler))
/// // Add middleware that inserts the state into all incoming request's
/// // extensions.
/// .layer(AddExtensionLayer::new(state));

View file

@ -37,7 +37,7 @@ use tower::{BoxError, Layer, Service};
/// use axum::{
/// extract::{extractor_middleware, FromRequest, RequestParts},
/// handler::{get, post},
/// route,
/// Router,
/// };
/// use http::StatusCode;
/// use async_trait::async_trait;
@ -76,7 +76,8 @@ use tower::{BoxError, Layer, Service};
/// // If we get here the request has been authorized
/// }
///
/// let app = route("/", get(handler))
/// let app = Router::new()
/// .route("/", get(handler))
/// .route("/foo", post(other_handler))
/// // The extractor will run before all routes
/// .layer(extractor_middleware::<RequireAuth>());

View file

@ -17,7 +17,7 @@ use tower::BoxError;
/// use axum::{
/// extract::Form,
/// handler::post,
/// route,
/// Router,
/// };
/// use serde::Deserialize;
///
@ -33,7 +33,7 @@ use tower::BoxError;
/// // ...
/// }
///
/// let app = route("/sign_up", post(accept_form));
/// let app = Router::new().route("/sign_up", post(accept_form));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };

View file

@ -11,7 +11,7 @@
//! use axum::{
//! Json,
//! handler::{post, Handler},
//! route,
//! Router,
//! };
//! use serde::Deserialize;
//!
@ -27,7 +27,7 @@
//! // ...
//! }
//!
//! let app = route("/users", post(create_user));
//! let app = Router::new().route("/users", post(create_user));
//! # async {
//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
//! # };
@ -42,7 +42,7 @@
//! async_trait,
//! extract::{FromRequest, RequestParts},
//! handler::get,
//! route,
//! Router,
//! };
//! use http::{StatusCode, header::{HeaderValue, USER_AGENT}};
//!
@ -72,7 +72,7 @@
//! // ...
//! }
//!
//! let app = route("/foo", get(handler));
//! let app = Router::new().route("/foo", get(handler));
//! # async {
//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
//! # };
@ -86,7 +86,7 @@
//! use axum::{
//! extract::{Path, Query},
//! handler::get,
//! route,
//! Router,
//! };
//! use std::collections::HashMap;
//!
@ -101,7 +101,7 @@
//! // ...
//! }
//!
//! let app = route("/foo", get(handler));
//! let app = Router::new().route("/foo", get(handler));
//! # async {
//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
//! # };
@ -118,7 +118,7 @@
//! use axum::{
//! extract::Json,
//! handler::post,
//! route,
//! Router,
//! };
//! use serde_json::Value;
//!
@ -130,7 +130,7 @@
//! }
//! }
//!
//! let app = route("/users", post(create_user));
//! let app = Router::new().route("/users", post(create_user));
//! # async {
//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
//! # };
@ -143,7 +143,7 @@
//! use axum::{
//! extract::{Json, rejection::JsonRejection},
//! handler::post,
//! route,
//! Router,
//! };
//! use serde_json::Value;
//!
@ -169,7 +169,7 @@
//! }
//! }
//!
//! let app = route("/users", post(create_user));
//! let app = Router::new().route("/users", post(create_user));
//! # async {
//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
//! # };
@ -184,7 +184,7 @@
//! use axum::{
//! extract::Json,
//! handler::post,
//! route,
//! Router,
//! };
//! use serde_json::Value;
//!
@ -192,7 +192,7 @@
//! // `value` is of type `Value`
//! }
//!
//! let app = route("/users", post(create_user));
//! let app = Router::new().route("/users", post(create_user));
//! # async {
//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
//! # };
@ -217,7 +217,7 @@
//! body::Body,
//! handler::get,
//! http::{header::HeaderMap, Request},
//! route,
//! Router,
//! };
//!
//! struct MyBody<B>(B);
@ -244,10 +244,10 @@
//! }
//! }
//!
//! let app =
//! // `String` works directly with any body type
//! route(
//! let app = Router::new()
//! .route(
//! "/string",
//! // `String` works directly with any body type
//! get(|_: String| async {})
//! )
//! .route(

View file

@ -23,7 +23,7 @@ use tower::BoxError;
/// use axum::{
/// extract::Multipart,
/// handler::post,
/// route,
/// Router,
/// };
/// use futures::stream::StreamExt;
///
@ -36,7 +36,7 @@ use tower::BoxError;
/// }
/// }
///
/// let app = route("/upload", post(upload));
/// let app = Router::new().route("/upload", post(upload));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };

View file

@ -14,7 +14,7 @@ use std::ops::{Deref, DerefMut};
/// use axum::{
/// extract::Path,
/// handler::get,
/// route,
/// Router,
/// };
/// use uuid::Uuid;
///
@ -24,7 +24,7 @@ use std::ops::{Deref, DerefMut};
/// // ...
/// }
///
/// let app = route("/users/:user_id/team/:team_id", get(users_teams_show));
/// let app = Router::new().route("/users/:user_id/team/:team_id", get(users_teams_show));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
@ -36,7 +36,7 @@ use std::ops::{Deref, DerefMut};
/// use axum::{
/// extract::Path,
/// handler::get,
/// route,
/// Router,
/// };
/// use uuid::Uuid;
///
@ -44,7 +44,7 @@ use std::ops::{Deref, DerefMut};
/// // ...
/// }
///
/// let app = route("/users/:user_id", get(user_info));
/// let app = Router::new().route("/users/:user_id", get(user_info));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
@ -57,7 +57,7 @@ use std::ops::{Deref, DerefMut};
/// use axum::{
/// extract::Path,
/// handler::get,
/// route,
/// Router,
/// };
/// use serde::Deserialize;
/// use uuid::Uuid;
@ -74,7 +74,7 @@ use std::ops::{Deref, DerefMut};
/// // ...
/// }
///
/// let app = route("/users/:user_id/team/:team_id", get(users_teams_show));
/// let app = Router::new().route("/users/:user_id/team/:team_id", get(users_teams_show));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };

View file

@ -13,7 +13,7 @@ use std::ops::Deref;
/// use axum::{
/// extract::Query,
/// handler::get,
/// route,
/// Router,
/// };
/// use serde::Deserialize;
///
@ -31,7 +31,7 @@ use std::ops::Deref;
/// // ...
/// }
///
/// let app = route("/list_things", get(list_things));
/// let app = Router::new().route("/list_things", get(list_things));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };

View file

@ -10,7 +10,7 @@ use std::convert::Infallible;
/// use axum::{
/// extract::RawQuery,
/// handler::get,
/// route,
/// Router,
/// };
/// use futures::StreamExt;
///
@ -18,7 +18,7 @@ use std::convert::Infallible;
/// // ...
/// }
///
/// let app = route("/users", get(handler));
/// let app = Router::new().route("/users", get(handler));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };

View file

@ -92,21 +92,21 @@ where
/// ```
/// use axum::{
/// handler::get,
/// route,
/// routing::nest,
/// Router,
/// extract::OriginalUri,
/// http::Uri
/// };
///
/// let api_routes = route(
/// "/users",
/// get(|uri: Uri, OriginalUri(original_uri): OriginalUri| async {
/// // `uri` is `/users`
/// // `original_uri` is `/api/users`
/// }),
/// );
/// let api_routes = Router::new()
/// .route(
/// "/users",
/// get(|uri: Uri, OriginalUri(original_uri): OriginalUri| async {
/// // `uri` is `/users`
/// // `original_uri` is `/api/users`
/// }),
/// );
///
/// let app = nest("/api", api_routes);
/// let app = Router::new().nest("/api", api_routes);
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
@ -174,7 +174,7 @@ where
/// use axum::{
/// extract::BodyStream,
/// handler::get,
/// route,
/// Router,
/// };
/// use futures::StreamExt;
///
@ -184,7 +184,7 @@ where
/// }
/// }
///
/// let app = route("/users", get(handler));
/// let app = Router::new().route("/users", get(handler));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
@ -227,7 +227,7 @@ where
/// use axum::{
/// extract::Body,
/// handler::get,
/// route,
/// Router,
/// };
/// use futures::StreamExt;
///
@ -235,7 +235,7 @@ where
/// // ...
/// }
///
/// let app = route("/users", get(handler));
/// let app = Router::new().route("/users", get(handler));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
@ -289,14 +289,14 @@ where
#[cfg(test)]
mod tests {
use super::*;
use crate::{body::Body, handler::post, route, tests::*};
use crate::{body::Body, handler::post, tests::*, Router};
use http::StatusCode;
#[tokio::test]
async fn multiple_request_extractors() {
async fn handler(_: Request<Body>, _: Request<Body>) {}
let app = route("/", post(handler));
let app = Router::new().route("/", post(handler));
let addr = run_in_background(app).await;

View file

@ -14,7 +14,7 @@ use std::{convert::Infallible, ops::Deref};
/// use axum::{
/// extract::TypedHeader,
/// handler::get,
/// route,
/// Router,
/// };
/// use headers::UserAgent;
///
@ -24,7 +24,7 @@ use std::{convert::Infallible, ops::Deref};
/// // ...
/// }
///
/// let app = route("/users/:user_id/team/:team_id", get(users_teams_show));
/// let app = Router::new().route("/users/:user_id/team/:team_id", get(users_teams_show));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
@ -125,7 +125,7 @@ impl std::error::Error for TypedHeaderRejection {
#[cfg(test)]
mod tests {
use super::*;
use crate::{handler::get, response::IntoResponse, route, tests::*};
use crate::{handler::get, response::IntoResponse, tests::*, Router};
#[tokio::test]
async fn typed_header() {
@ -135,7 +135,7 @@ mod tests {
user_agent.to_string()
}
let app = route("/", get(handle));
let app = Router::new().route("/", get(handle));
let addr = run_in_background(app).await;

View file

@ -7,10 +7,10 @@
//! extract::ws::{WebSocketUpgrade, WebSocket},
//! handler::get,
//! response::IntoResponse,
//! route,
//! Router,
//! };
//!
//! let app = route("/ws", get(handler));
//! let app = Router::new().route("/ws", get(handler));
//!
//! async fn handler(ws: WebSocketUpgrade) -> impl IntoResponse {
//! ws.on_upgrade(handle_socket)
@ -113,10 +113,10 @@ impl WebSocketUpgrade {
/// extract::ws::{WebSocketUpgrade, WebSocket},
/// handler::get,
/// response::IntoResponse,
/// route,
/// Router,
/// };
///
/// let app = route("/ws", get(handler));
/// let app = Router::new().route("/ws", get(handler));
///
/// async fn handler(ws: WebSocketUpgrade) -> impl IntoResponse {
/// ws.protocols(["graphql-ws", "graphql-transport-ws"])

View file

@ -33,13 +33,13 @@ pub use self::into_service::IntoService;
/// ```rust
/// use axum::{
/// handler::any,
/// route,
/// Router,
/// };
///
/// async fn handler() {}
///
/// // All requests to `/` will go to `handler` regardless of the HTTP method.
/// let app = route("/", any(handler));
/// let app = Router::new().route("/", any(handler));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
@ -78,13 +78,13 @@ where
/// ```rust
/// use axum::{
/// handler::get,
/// route,
/// Router,
/// };
///
/// async fn handler() {}
///
/// // Requests to `GET /` will go to `handler`.
/// let app = route("/", get(handler));
/// let app = Router::new().route("/", get(handler));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
@ -167,14 +167,14 @@ where
/// ```rust
/// use axum::{
/// handler::on,
/// route,
/// Router,
/// routing::MethodFilter,
/// };
///
/// async fn handler() {}
///
/// // Requests to `POST /` will go to `handler`.
/// let app = route("/", on(MethodFilter::POST, handler));
/// let app = Router::new().route("/", on(MethodFilter::POST, handler));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
@ -234,14 +234,14 @@ pub trait Handler<B, T>: Clone + Send + Sized + 'static {
/// ```rust
/// use axum::{
/// handler::{get, Handler},
/// route,
/// Router,
/// };
/// use tower::limit::{ConcurrencyLimitLayer, ConcurrencyLimit};
///
/// async fn handler() { /* ... */ }
///
/// let layered_handler = handler.layer(ConcurrencyLimitLayer::new(64));
/// let app = route("/", get(layered_handler));
/// let app = Router::new().route("/", get(layered_handler));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
@ -511,7 +511,7 @@ impl<H, B, T, F> OnMethod<H, B, T, F> {
/// # Example
///
/// ```rust
/// use axum::{handler::post, route};
/// use axum::{handler::post, Router};
///
/// async fn handler() {}
///
@ -519,7 +519,7 @@ impl<H, B, T, F> OnMethod<H, B, T, F> {
///
/// // Requests to `GET /` will go to `handler` and `POST /` will go to
/// // `other_handler`.
/// let app = route("/", post(handler).get(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();
/// # };
@ -603,7 +603,7 @@ impl<H, B, T, F> OnMethod<H, B, T, F> {
/// ```rust
/// use axum::{
/// handler::get,
/// route,
/// Router,
/// routing::MethodFilter
/// };
///
@ -613,7 +613,7 @@ impl<H, B, T, F> OnMethod<H, B, T, F> {
///
/// // Requests to `GET /` will go to `handler` and `DELETE /` will go to
/// // `other_handler`
/// let app = route("/", get(handler).on(MethodFilter::DELETE, 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();
/// # };

View file

@ -30,7 +30,7 @@ use tower::BoxError;
/// use axum::{
/// extract,
/// handler::post,
/// route,
/// Router,
/// };
/// use serde::Deserialize;
///
@ -44,7 +44,7 @@ use tower::BoxError;
/// // payload is a `CreateUser`
/// }
///
/// let app = route("/users", post(create_user));
/// let app = Router::new().route("/users", post(create_user));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
@ -59,7 +59,7 @@ use tower::BoxError;
/// use axum::{
/// extract::Path,
/// handler::get,
/// route,
/// Router,
/// Json,
/// };
/// use serde::Serialize;
@ -81,7 +81,7 @@ use tower::BoxError;
/// # unimplemented!()
/// }
///
/// let app = route("/users/:id", get(get_user));
/// let app = Router::new().route("/users/:id", get(get_user));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };

View file

@ -48,13 +48,13 @@
//! ```rust,no_run
//! use axum::{
//! handler::get,
//! route,
//! Router,
//! };
//!
//! #[tokio::main]
//! async fn main() {
//! // build our application with a single route
//! let app = route("/", get(|| async { "Hello, World!" }));
//! let app = Router::new().route("/", get(|| async { "Hello, World!" }));
//!
//! // run it with hyper on localhost:3000
//! axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
@ -105,10 +105,11 @@
//! ```rust,no_run
//! use axum::{
//! handler::get,
//! route,
//! Router,
//! };
//!
//! let app = route("/", get(get_slash).post(post_slash))
//! let app = Router::new()
//! .route("/", get(get_slash).post(post_slash))
//! .route("/foo", get(get_foo));
//!
//! async fn get_slash() {
@ -142,7 +143,7 @@
//! body::{Body, BoxBody},
//! handler::get,
//! http::Request,
//! route,
//! Router,
//! };
//! use tower::{Service, ServiceExt};
//! use http::{Method, Response, StatusCode};
@ -152,7 +153,8 @@
//! # async fn main() {
//! // `/foo` also matches `/:key` so adding the routes in this order means `/foo`
//! // will be inaccessible.
//! let mut app = route("/foo", get(|| async { "/foo called" }))
//! let mut app = Router::new()
//! .route("/foo", get(|| async { "/foo called" }))
//! .route("/:key", get(|| async { "/:key called" }));
//!
//! // Even though we use `/foo` as the request URI, `/:key` takes precedence
@ -163,7 +165,8 @@
//!
//! // We have to add `/foo` after `/:key` since routes are matched bottom to
//! // top.
//! let mut new_app = route("/:key", get(|| async { "/:key called" }))
//! let mut new_app = Router::new()
//! .route("/:key", get(|| async { "/:key called" }))
//! .route("/foo", get(|| async { "/foo called" }));
//!
//! // Now it works
@ -207,15 +210,15 @@
//!
//! ```rust,no_run
//! use axum::{
//! route,
//! Router,
//! handler::{get, post},
//! };
//!
//! // `GET /` and `POST /` are both accepted
//! let app = route("/", get(handler).post(handler));
//! let app = Router::new().route("/", get(handler).post(handler));
//!
//! // This will _not_ work. Only `POST /` will be accessible.
//! let wont_work = route("/", get(handler)).route("/", post(handler));
//! let wont_work = Router::new().route("/", get(handler)).route("/", post(handler));
//!
//! async fn handler() {}
//! # async {
@ -232,7 +235,7 @@
//! use axum::{
//! body::Body,
//! http::Request,
//! route,
//! Router,
//! service
//! };
//! use tower_http::services::ServeFile;
@ -240,31 +243,34 @@
//! use std::convert::Infallible;
//! use tower::service_fn;
//!
//! let app = route(
//! // Any request to `/` goes to a service
//! "/",
//! // Services who's response body is not `axum::body::BoxBody`
//! // can be wrapped in `axum::service::any` (or one of the other routing filters)
//! // to have the response body mapped
//! service::any(service_fn(|_: Request<Body>| async {
//! let res = Response::new(Body::from("Hi from `GET /`"));
//! Ok(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::box_body(body);
//! let res = Response::new(body);
//! Ok(res)
//! })
//! ).route(
//! // GET `/static/Cargo.toml` goes to a service from tower-http
//! "/static/Cargo.toml",
//! service::get(ServeFile::new("Cargo.toml"))
//! );
//! let app = Router::new()
//! .route(
//! // Any request to `/` goes to a service
//! "/",
//! // Services who's response body is not `axum::body::BoxBody`
//! // can be wrapped in `axum::service::any` (or one of the other routing filters)
//! // to have the response body mapped
//! service::any(service_fn(|_: Request<Body>| async {
//! let res = Response::new(Body::from("Hi from `GET /`"));
//! Ok(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::box_body(body);
//! let res = Response::new(body);
//! Ok(res)
//! })
//! )
//! .route(
//! // GET `/static/Cargo.toml` goes to a service from tower-http
//! "/static/Cargo.toml",
//! service::get(ServeFile::new("Cargo.toml"))
//! );
//! # async {
//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
//! # };
@ -275,24 +281,27 @@
//!
//! ## Nesting Routes
//!
//! Routes can be nested by calling [`nest`](routing::nest):
//! Routes can be nested by calling [`Router::nest`](routing::Router::nest):
//!
//! ```rust,no_run
//! use axum::{
//! body::{Body, BoxBody},
//! http::Request,
//! handler::get,
//! route,
//! routing::{BoxRoute, Router}
//! Router,
//! routing::BoxRoute
//! };
//! use tower_http::services::ServeFile;
//! use http::Response;
//!
//! fn api_routes() -> Router<BoxRoute> {
//! route("/users", get(|_: Request<Body>| async { /* ... */ })).boxed()
//! Router::new()
//! .route("/users", get(|_: Request<Body>| async { /* ... */ }))
//! .boxed()
//! }
//!
//! let app = route("/", get(|_: Request<Body>| async { /* ... */ }))
//! let app = Router::new()
//! .route("/", get(|_: Request<Body>| async { /* ... */ }))
//! .nest("/api", api_routes());
//! # async {
//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
@ -316,11 +325,11 @@
//! use axum::{
//! extract,
//! handler::post,
//! route,
//! Router,
//! };
//! use serde::Deserialize;
//!
//! let app = route("/users", post(create_user));
//! let app = Router::new().route("/users", post(create_user));
//!
//! #[derive(Deserialize)]
//! struct CreateUser {
@ -346,11 +355,11 @@
//! use axum::{
//! extract,
//! handler::post,
//! route,
//! Router,
//! };
//! use uuid::Uuid;
//!
//! let app = route("/users/:id", post(create_user));
//! let app = Router::new().route("/users/:id", post(create_user));
//!
//! async fn create_user(extract::Path(user_id): extract::Path<Uuid>) {
//! // ...
@ -366,12 +375,12 @@
//! use axum::{
//! extract,
//! handler::get,
//! route,
//! Router,
//! };
//! use uuid::Uuid;
//! use serde::Deserialize;
//!
//! let app = route("/users/:id/things", get(get_user_things));
//! let app = Router::new().route("/users/:id/things", get(get_user_things));
//!
//! #[derive(Deserialize)]
//! struct Pagination {
@ -405,10 +414,10 @@
//! body::Body,
//! handler::post,
//! http::Request,
//! route,
//! Router,
//! };
//!
//! let app = route("/users/:id", post(handler));
//! let app = Router::new().route("/users/:id", post(handler));
//!
//! async fn handler(req: Request<Body>) {
//! // ...
@ -437,7 +446,7 @@
//! handler::{get, Handler},
//! http::Request,
//! response::{Html, Json},
//! route,
//! Router,
//! };
//! use http::{StatusCode, Response, Uri};
//! use serde_json::{Value, json};
@ -493,7 +502,8 @@
//! Response::builder().body(Body::empty()).unwrap()
//! }
//!
//! let app = route("/plain_text", get(plain_text))
//! let app = Router::new()
//! .route("/plain_text", get(plain_text))
//! .route("/plain_text_string", get(plain_text_string))
//! .route("/bytes", get(bytes))
//! .route("/empty", get(empty))
@ -520,14 +530,15 @@
//! ```rust,no_run
//! use axum::{
//! handler::{get, Handler},
//! route,
//! Router,
//! };
//! use tower::limit::ConcurrencyLimitLayer;
//!
//! let app = route(
//! "/",
//! get(handler.layer(ConcurrencyLimitLayer::new(100))),
//! );
//! let app = Router::new()
//! .route(
//! "/",
//! get(handler.layer(ConcurrencyLimitLayer::new(100))),
//! );
//!
//! async fn handler() {}
//! # async {
@ -542,11 +553,12 @@
//! ```rust,no_run
//! use axum::{
//! handler::{get, post},
//! route,
//! Router,
//! };
//! use tower::limit::ConcurrencyLimitLayer;
//!
//! let app = route("/", get(get_slash))
//! let app = Router::new()
//! .route("/", get(get_slash))
//! .route("/foo", post(post_foo))
//! .layer(ConcurrencyLimitLayer::new(100));
//!
@ -575,7 +587,7 @@
//! ```rust,no_run
//! use axum::{
//! handler::{get, Handler},
//! route,
//! Router,
//! };
//! use tower::{
//! BoxError, timeout::{TimeoutLayer, error::Elapsed},
@ -583,30 +595,31 @@
//! use std::{borrow::Cow, time::Duration, convert::Infallible};
//! use http::StatusCode;
//!
//! let app = route(
//! "/",
//! get(handle
//! .layer(TimeoutLayer::new(Duration::from_secs(30)))
//! // `Timeout` uses `BoxError` as the error type
//! .handle_error(|error: BoxError| {
//! // Check if the actual error type is `Elapsed` which
//! // `Timeout` returns
//! if error.is::<Elapsed>() {
//! return Ok::<_, Infallible>((
//! StatusCode::REQUEST_TIMEOUT,
//! "Request took too long".into(),
//! ));
//! }
//! let app = Router::new()
//! .route(
//! "/",
//! get(handle
//! .layer(TimeoutLayer::new(Duration::from_secs(30)))
//! // `Timeout` uses `BoxError` as the error type
//! .handle_error(|error: BoxError| {
//! // Check if the actual error type is `Elapsed` which
//! // `Timeout` returns
//! if error.is::<Elapsed>() {
//! return Ok::<_, Infallible>((
//! StatusCode::REQUEST_TIMEOUT,
//! "Request took too long".into(),
//! ));
//! }
//!
//! // If we encounter some error we don't handle return a generic
//! // error
//! return Ok::<_, Infallible>((
//! StatusCode::INTERNAL_SERVER_ERROR,
//! // `Cow` lets us return either `&str` or `String`
//! Cow::from(format!("Unhandled internal error: {}", error)),
//! ));
//! })),
//! );
//! // If we encounter some error we don't handle return a generic
//! // error
//! return Ok::<_, Infallible>((
//! StatusCode::INTERNAL_SERVER_ERROR,
//! // `Cow` lets us return either `&str` or `String`
//! Cow::from(format!("Unhandled internal error: {}", error)),
//! ));
//! })),
//! );
//!
//! async fn handle() {}
//! # async {
@ -629,7 +642,7 @@
//! body::Body,
//! handler::get,
//! http::Request,
//! route,
//! Router,
//! };
//! use tower::ServiceBuilder;
//! use tower_http::compression::CompressionLayer;
@ -646,7 +659,8 @@
//! .layer(CompressionLayer::new())
//! .into_inner();
//!
//! let app = route("/", get(|_: Request<Body>| async { /* ... */ }))
//! let app = Router::new()
//! .route("/", get(|_: Request<Body>| async { /* ... */ }))
//! .layer(middleware_stack);
//! # async {
//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
@ -665,7 +679,7 @@
//! AddExtensionLayer,
//! extract,
//! handler::get,
//! route,
//! Router,
//! };
//! use std::sync::Arc;
//!
@ -675,7 +689,9 @@
//!
//! let shared_state = Arc::new(State { /* ... */ });
//!
//! let app = route("/", get(handler)).layer(AddExtensionLayer::new(shared_state));
//! let app = Router::new()
//! .route("/", get(handler))
//! .layer(AddExtensionLayer::new(shared_state));
//!
//! async fn handler(
//! state: extract::Extension<Arc<State>>,
@ -738,6 +754,8 @@
//! [`Router::or`]: crate::routing::Router::or
//! [`axum::Server`]: hyper::server::Server
//! [`OriginalUri`]: crate::extract::OriginalUri
//! [`Service`]: tower::Service
//! [`Service::poll_ready`]: tower::Service::poll_ready
#![warn(
clippy::all,
@ -779,10 +797,6 @@
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(test, allow(clippy::float_cmp))]
use http::Request;
use routing::{EmptyRouter, Route};
use tower::Service;
#[macro_use]
pub(crate) mod macros;
@ -810,56 +824,4 @@ pub use hyper::Server;
#[doc(no_inline)]
pub use tower_http::add_extension::{AddExtension, AddExtensionLayer};
pub use self::{error::Error, json::Json};
/// Create a route.
///
/// `description` is a string of path segments separated by `/`. Each segment
/// can be either concrete or a capture:
///
/// - `/foo/bar/baz` will only match requests where the path is `/foo/bar/bar`.
/// - `/:foo` will match any route with exactly one segment _and_ it will
/// capture the first segment and store it at the key `foo`.
///
/// `service` is the [`Service`] that should receive the request if the path
/// matches `description`.
///
/// # Examples
///
/// ```rust
/// use axum::{
/// body::Body,
/// http::Request,
/// route
/// };
///
/// # use std::convert::Infallible;
/// # use http::Response;
/// # let service = tower::service_fn(|_: Request<Body>| async {
/// # Ok::<Response<Body>, Infallible>(Response::new(Body::empty()))
/// # });
///
/// route("/", service);
/// route("/users", service);
/// route("/users/:id", service);
/// route("/api/:version/users/:id/action", service);
/// ```
///
/// # Panics
///
/// Panics if `description` doesn't start with `/`.
pub fn route<S, B>(
description: &str,
service: S,
) -> routing::Router<Route<S, EmptyRouter<S::Error>>>
where
S: Service<Request<B>> + Clone,
{
routing::Router::new().route(description, service)
}
mod sealed {
#![allow(unreachable_pub, missing_docs)]
pub trait Sealed {}
}
pub use self::{error::Error, json::Json, routing::Router};

View file

@ -13,7 +13,7 @@ use tower::{util::Either, BoxError};
///
/// ```rust
/// use axum::{
/// route,
/// Router,
/// response::{IntoResponse, Headers},
/// handler::get,
/// };
@ -36,7 +36,8 @@ use tower::{util::Either, BoxError};
///
/// // Or `[(&str, &str)]` if you're on Rust 1.53+
///
/// let app = route("/just-headers", get(just_headers))
/// let app = Router::new()
/// .route("/just-headers", get(just_headers))
/// .route("/from-strings", get(from_strings));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();

View file

@ -44,7 +44,7 @@ pub use self::{
/// use axum::{
/// handler::get,
/// response::IntoResponse,
/// route,
/// Router,
/// };
/// use http_body::Body;
/// use http::{Response, HeaderMap};
@ -94,7 +94,7 @@ pub use self::{
/// // covered by a blanket implementation in axum.
///
/// // `MyBody` can now be returned from handlers.
/// let app = route("/", get(|| async { MyBody }));
/// let app = Router::new().route("/", get(|| async { MyBody }));
/// # async {
/// # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };

View file

@ -12,10 +12,11 @@ use std::convert::TryFrom;
/// use axum::{
/// handler::get,
/// response::Redirect,
/// route,
/// Router,
/// };
///
/// let app = route("/old", get(|| async { Redirect::permanent("/new".parse().unwrap()) }))
/// let app = Router::new()
/// .route("/old", get(|| async { Redirect::permanent("/new".parse().unwrap()) }))
/// .route("/new", get(|| async { "Hello!" }));
/// # async {
/// # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();

View file

@ -5,14 +5,14 @@
//! ```
//! use axum::{
//! handler::get,
//! route,
//! Router,
//! };
//! use axum::response::sse::{sse, Event, KeepAlive, Sse};
//! use std::{time::Duration, convert::Infallible};
//! use tokio_stream::StreamExt as _ ;
//! use futures::stream::{self, Stream};
//!
//! let app = route("/sse", get(sse_handler));
//! let app = Router::new().route("/sse", get(sse_handler));
//!
//! async fn sse_handler() -> Sse<impl Stream<Item = Result<Event, Infallible>>> {
//! // A `Stream` that repeats an event every second

View file

@ -81,28 +81,44 @@ where
impl<S> Router<S> {
/// Add another route to the router.
///
/// `description` is a string of path segments separated by `/`. Each segment
/// can be either concrete or a capture:
///
/// - `/foo/bar/baz` will only match requests where the path is `/foo/bar/bar`.
/// - `/:foo` will match any route with exactly one segment _and_ it will
/// capture the first segment and store it at the key `foo`.
///
/// `service` is the [`Service`] that should receive the request if the path
/// matches `description`.
///
/// # Example
///
/// ```rust
/// use axum::{
/// handler::get,
/// route,
/// };
/// use axum::{handler::{get, delete}, Router};
///
/// async fn first_handler() { /* ... */ }
/// let app = Router::new()
/// .route("/", get(root))
/// .route("/users", get(list_users).post(create_user))
/// .route("/users/:id", get(show_user))
/// .route("/api/:version/users/:id/action", delete(do_thing));
///
/// async fn second_handler() { /* ... */ }
/// async fn root() { /* ... */ }
///
/// async fn third_handler() { /* ... */ }
/// async fn list_users() { /* ... */ }
///
/// // `GET /` goes to `first_handler`, `POST /` goes to `second_handler`,
/// // and `GET /foo` goes to third_handler.
/// let app = route("/", get(first_handler).post(second_handler))
/// .route("/foo", get(third_handler));
/// async fn create_user() { /* ... */ }
///
/// async fn show_user() { /* ... */ }
///
/// async fn do_thing() { /* ... */ }
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
///
/// # Panics
///
/// Panics if `description` doesn't start with `/`.
pub fn route<T, B>(self, description: &str, svc: T) -> Router<Route<T, S>>
where
T: Service<Request<B>> + Clone,
@ -114,9 +130,83 @@ impl<S> Router<S> {
})
}
/// Nest another service inside this router at the given path.
/// Nest a group of routes (or a [`Service`]) at some path.
///
/// See [`nest`] for more details.
/// This allows you to break your application into smaller pieces and compose
/// them together.
///
/// ```
/// use axum::{
/// handler::get,
/// Router,
/// };
/// use http::Uri;
///
/// async fn users_get(uri: Uri) {
/// // `users_get` will still see the whole URI.
/// assert_eq!(uri.path(), "/api/users");
/// }
///
/// async fn users_post() {}
///
/// async fn careers() {}
///
/// let users_api = Router::new().route("/users", get(users_get).post(users_post));
///
/// let app = Router::new().nest("/api", users_api).route("/careers", get(careers));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
///
/// Take care when using `nest` together with dynamic routes as nesting also
/// captures from the outer routes:
///
/// ```
/// use axum::{
/// extract::Path,
/// handler::get,
/// Router,
/// };
/// use std::collections::HashMap;
///
/// async fn users_get(Path(params): Path<HashMap<String, String>>) {
/// // Both `version` and `id` were captured even though `users_api` only
/// // explicitly captures `id`.
/// let version = params.get("version");
/// let id = params.get("id");
/// }
///
/// let users_api = Router::new().route("/users/:id", get(users_get));
///
/// let app = Router::new().nest("/:version/api", users_api);
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
///
/// `nest` also accepts any [`Service`]. This can for example be used with
/// [`tower_http::services::ServeDir`] to serve static files from a directory:
///
/// ```
/// use axum::{
/// Router,
/// service::get,
/// };
/// use tower_http::services::ServeDir;
///
/// // Serves files inside the `public` directory at `GET /public/*`
/// let serve_dir_service = ServeDir::new("public");
///
/// let app = Router::new().nest("/public", get(serve_dir_service));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
///
/// If necessary you can use [`Router::boxed`] to box a group of routes
/// making the type easier to name. This is sometimes useful when working with
/// `nest`.
pub fn nest<T, B>(self, description: &str, svc: T) -> Router<Nested<T, S>>
where
T: Service<Request<B>> + Clone,
@ -137,8 +227,8 @@ impl<S> Router<S> {
/// use axum::{
/// body::Body,
/// handler::get,
/// route,
/// routing::{Router, BoxRoute}
/// Router,
/// routing::BoxRoute
/// };
///
/// async fn first_handler() { /* ... */ }
@ -148,7 +238,8 @@ impl<S> Router<S> {
/// async fn third_handler() { /* ... */ }
///
/// fn app() -> Router<BoxRoute> {
/// route("/", get(first_handler).post(second_handler))
/// Router::new()
/// .route("/", get(first_handler).post(second_handler))
/// .route("/foo", get(third_handler))
/// .boxed()
/// }
@ -195,7 +286,7 @@ impl<S> Router<S> {
/// ```rust
/// use axum::{
/// handler::get,
/// route,
/// Router,
/// };
/// use tower::limit::{ConcurrencyLimitLayer, ConcurrencyLimit};
///
@ -207,7 +298,7 @@ impl<S> Router<S> {
///
/// // All requests to `handler` and `other_handler` will be sent through
/// // `ConcurrencyLimit`
/// let app = route("/", get(first_handler))
/// let app = Router::new().route("/", get(first_handler))
/// .route("/foo", get(second_handler))
/// .layer(ConcurrencyLimitLayer::new(64))
/// // Request to `GET /bar` will go directly to `third_handler` and
@ -224,7 +315,7 @@ impl<S> Router<S> {
/// ```rust
/// use axum::{
/// handler::get,
/// route,
/// Router,
/// };
/// use tower_http::trace::TraceLayer;
///
@ -234,7 +325,8 @@ impl<S> Router<S> {
///
/// async fn third_handler() { /* ... */ }
///
/// let app = route("/", get(first_handler))
/// let app = Router::new()
/// .route("/", get(first_handler))
/// .route("/foo", get(second_handler))
/// .route("/bar", get(third_handler))
/// .layer(TraceLayer::new_for_http());
@ -258,10 +350,10 @@ impl<S> Router<S> {
/// ```
/// use axum::{
/// handler::get,
/// route,
/// Router,
/// };
///
/// let app = route("/", get(|| async { "Hi!" }));
/// let app = Router::new().route("/", get(|| async { "Hi!" }));
///
/// # async {
/// axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
@ -291,11 +383,11 @@ impl<S> Router<S> {
/// use axum::{
/// extract::ConnectInfo,
/// handler::get,
/// route,
/// Router,
/// };
/// use std::net::SocketAddr;
///
/// let app = route("/", get(handler));
/// let app = Router::new().route("/", get(handler));
///
/// async fn handler(ConnectInfo(addr): ConnectInfo<SocketAddr>) -> String {
/// format!("Hello {}", addr)
@ -317,11 +409,11 @@ impl<S> Router<S> {
/// use axum::{
/// extract::connect_info::{ConnectInfo, Connected},
/// handler::get,
/// route,
/// Router,
/// };
/// use hyper::server::conn::AddrStream;
///
/// let app = route("/", get(handler));
/// let app = Router::new().route("/", get(handler));
///
/// async fn handler(
/// ConnectInfo(my_connect_info): ConnectInfo<MyConnectInfo>,
@ -379,7 +471,7 @@ impl<S> Router<S> {
/// ```
/// use axum::{
/// handler::get,
/// route,
/// Router,
/// };
/// #
/// # async fn users_list() {}
@ -387,10 +479,10 @@ impl<S> Router<S> {
/// # async fn teams_list() {}
///
/// // define some routes separately
/// let user_routes = route("/users", get(users_list))
/// let user_routes = Router::new().route("/users", get(users_list))
/// .route("/users/:id", get(users_show));
///
/// let team_routes = route("/teams", get(teams_list));
/// let team_routes = Router::new().route("/teams", get(teams_list));
///
/// // combine them into one
/// let app = user_routes.or(team_routes);
@ -416,13 +508,13 @@ impl<S> Router<S> {
/// use axum::{
/// handler::get,
/// http::StatusCode,
/// route,
/// Router,
/// };
/// use tower::{BoxError, timeout::TimeoutLayer};
/// use std::{time::Duration, convert::Infallible};
///
/// // This router can never fail, since handlers can never fail.
/// let app = route("/", get(|| async {}));
/// let app = Router::new().route("/", get(|| async {}));
///
/// // Now the router can fail since the `tower::timeout::Timeout`
/// // middleware will return an error if the timeout elapses.
@ -455,12 +547,13 @@ impl<S> Router<S> {
/// use axum::{
/// handler::get,
/// http::StatusCode,
/// route,
/// Router,
/// };
/// use tower::{BoxError, timeout::TimeoutLayer};
/// use std::time::Duration;
///
/// let app = route("/", get(|| async {}))
/// let app = Router::new()
/// .route("/", get(|| async {}))
/// .layer(TimeoutLayer::new(Duration::from_secs(10)))
/// .handle_error(|error: BoxError| {
/// if error.is::<tower::timeout::error::Elapsed>() {
@ -498,8 +591,6 @@ impl<S> Router<S> {
/// A route that sends requests to one of two [`Service`]s depending on the
/// path.
///
/// Created with [`route`](crate::route). See that function for more details.
#[derive(Debug, Clone)]
pub struct Route<S, F> {
pub(crate) pattern: PathPattern,
@ -845,95 +936,9 @@ where
}
}
/// Nest a group of routes (or a [`Service`]) at some path.
///
/// This allows you to break your application into smaller pieces and compose
/// them together.
///
/// ```
/// use axum::{
/// handler::get,
/// route,
/// routing::nest,
/// };
/// use http::Uri;
///
/// async fn users_get(uri: Uri) {
/// // `users_get` will still see the whole URI.
/// assert_eq!(uri.path(), "/api/users");
/// }
///
/// async fn users_post() {}
///
/// async fn careers() {}
///
/// let users_api = route("/users", get(users_get).post(users_post));
///
/// let app = nest("/api", users_api).route("/careers", get(careers));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
///
/// Take care when using `nest` together with dynamic routes as nesting also
/// captures from the outer routes:
///
/// ```
/// use axum::{
/// extract::Path,
/// handler::get,
/// route,
/// routing::nest,
/// };
/// use std::collections::HashMap;
///
/// async fn users_get(Path(params): Path<HashMap<String, String>>) {
/// // Both `version` and `id` were captured even though `users_api` only
/// // explicitly captures `id`.
/// let version = params.get("version");
/// let id = params.get("id");
/// }
///
/// let users_api = route("/users/:id", get(users_get));
///
/// let app = nest("/:version/api", users_api);
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
///
/// `nest` also accepts any [`Service`]. This can for example be used with
/// [`tower_http::services::ServeDir`] to serve static files from a directory:
///
/// ```
/// use axum::{
/// routing::nest,
/// service::get,
/// };
/// use tower_http::services::ServeDir;
///
/// // Serves files inside the `public` directory at `GET /public/*`
/// let serve_dir_service = ServeDir::new("public");
///
/// let app = nest("/public", get(serve_dir_service));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
///
/// If necessary you can use [`Router::boxed`] to box a group of routes
/// making the type easier to name. This is sometimes useful when working with
/// `nest`.
pub fn nest<S, B>(description: &str, svc: S) -> Router<Nested<S, EmptyRouter<S::Error>>>
where
S: Service<Request<B>> + Clone,
{
Router::new().nest(description, svc)
}
/// A [`Service`] that has been nested inside a router at some path.
///
/// Created with [`nest`] or [`Router::nest`].
/// Created with [`Router::nest`].
#[derive(Debug, Clone)]
pub struct Nested<S, F> {
pattern: PathPattern,

View file

@ -15,7 +15,7 @@
//! body::Body,
//! handler::get,
//! http::Request,
//! route,
//! Router,
//! service,
//! };
//!
@ -23,7 +23,8 @@
//!
//! let redirect_service = Redirect::<Body>::permanent("/new".parse().unwrap());
//!
//! let app = route("/old", service::get(redirect_service))
//! let app = Router::new()
//! .route("/old", service::get(redirect_service))
//! .route("/new", get(handler));
//! # async {
//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
@ -66,7 +67,7 @@
//! ```rust
//! use axum::{
//! handler::get,
//! route,
//! Router,
//! };
//! use tower::ServiceBuilder;
//! # let some_backpressure_sensitive_middleware =
@ -74,7 +75,7 @@
//!
//! async fn handler() { /* ... */ }
//!
//! let app = route("/", get(handler));
//! let app = Router::new().route("/", get(handler));
//!
//! let app = ServiceBuilder::new()
//! .layer(some_backpressure_sensitive_middleware)
@ -148,7 +149,7 @@ where
/// ```rust
/// use axum::{
/// http::Request,
/// route,
/// Router,
/// service,
/// };
/// use http::Response;
@ -160,7 +161,7 @@ where
/// });
///
/// // Requests to `GET /` will go to `service`.
/// let app = route("/", service::get(service));
/// let app = Router::new().route("/", service::get(service));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
@ -245,7 +246,7 @@ where
/// http::Request,
/// handler::on,
/// service,
/// route,
/// Router,
/// routing::MethodFilter,
/// };
/// use http::Response;
@ -257,7 +258,7 @@ where
/// });
///
/// // Requests to `POST /` will go to `service`.
/// let app = route("/", service::on(MethodFilter::POST, service));
/// let app = Router::new().route("/", service::on(MethodFilter::POST, service));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
@ -340,7 +341,7 @@ impl<S, F, B> OnMethod<S, F, B> {
/// http::Request,
/// handler::on,
/// service,
/// route,
/// Router,
/// routing::MethodFilter,
/// };
/// use http::Response;
@ -357,7 +358,7 @@ impl<S, F, B> OnMethod<S, F, B> {
///
/// // Requests to `GET /` will go to `service` and `POST /` will go to
/// // `other_service`.
/// let app = route("/", service::post(service).get(other_service));
/// let app = Router::new().route("/", service::post(service).get(other_service));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
@ -443,7 +444,7 @@ impl<S, F, B> OnMethod<S, F, B> {
/// http::Request,
/// handler::on,
/// service,
/// route,
/// Router,
/// routing::MethodFilter,
/// };
/// use http::Response;
@ -459,7 +460,7 @@ impl<S, F, B> OnMethod<S, F, B> {
/// });
///
/// // Requests to `DELETE /` will go to `service`
/// let app = route("/", service::on(MethodFilter::DELETE, service));
/// let app = Router::new().route("/", service::on(MethodFilter::DELETE, service));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };

View file

@ -7,7 +7,7 @@ mod for_handlers {
#[tokio::test]
async fn get_handles_head() {
let app = route(
let app = Router::new().route(
"/",
get(|| async {
let mut headers = HeaderMap::new();
@ -43,7 +43,7 @@ mod for_services {
#[tokio::test]
async fn get_handles_head() {
let app = route(
let app = Router::new().route(
"/",
get(service_fn(|_req: Request<Body>| async move {
let res = Response::builder()

View file

@ -41,7 +41,7 @@ fn handle_error<E>(_: E) -> Result<StatusCode, Infallible> {
#[tokio::test]
async fn handler() {
let app = route(
let app = Router::new().route(
"/",
get(forever
.layer(timeout())
@ -62,7 +62,7 @@ async fn handler() {
#[tokio::test]
async fn handler_multiple_methods_first() {
let app = route(
let app = Router::new().route(
"/",
get(forever
.layer(timeout())
@ -84,7 +84,7 @@ async fn handler_multiple_methods_first() {
#[tokio::test]
async fn handler_multiple_methods_middle() {
let app = route(
let app = Router::new().route(
"/",
delete(unit)
.get(
@ -109,7 +109,7 @@ async fn handler_multiple_methods_middle() {
#[tokio::test]
async fn handler_multiple_methods_last() {
let app = route(
let app = Router::new().route(
"/",
delete(unit).get(
forever
@ -132,21 +132,22 @@ async fn handler_multiple_methods_last() {
#[test]
fn service_propagates_errors() {
let app = route::<_, Body>("/echo", service::post(Svc));
let app = Router::new().route::<_, Body>("/echo", service::post(Svc));
check_make_svc::<_, _, _, hyper::Error>(app.into_make_service());
}
#[test]
fn service_nested_propagates_errors() {
let app = route::<_, Body>("/echo", nest("/foo", service::post(Svc)));
let app =
Router::new().route::<_, Body>("/echo", Router::new().nest("/foo", service::post(Svc)));
check_make_svc::<_, _, _, hyper::Error>(app.into_make_service());
}
#[test]
fn service_handle_on_method() {
let app = route::<_, Body>(
let app = Router::new().route::<_, Body>(
"/echo",
service::get(Svc).handle_error(handle_error::<hyper::Error>),
);
@ -156,7 +157,7 @@ fn service_handle_on_method() {
#[test]
fn service_handle_on_method_multiple() {
let app = route::<_, Body>(
let app = Router::new().route::<_, Body>(
"/echo",
service::get(Svc)
.post(Svc)
@ -168,15 +169,17 @@ fn service_handle_on_method_multiple() {
#[test]
fn service_handle_on_router() {
let app =
route::<_, Body>("/echo", service::get(Svc)).handle_error(handle_error::<hyper::Error>);
let app = Router::new()
.route::<_, Body>("/echo", service::get(Svc))
.handle_error(handle_error::<hyper::Error>);
check_make_svc::<_, _, _, Infallible>(app.into_make_service());
}
#[test]
fn service_handle_on_router_still_impls_routing_dsl() {
let app = route::<_, Body>("/echo", service::get(Svc))
let app = Router::new()
.route::<_, Body>("/echo", service::get(Svc))
.handle_error(handle_error::<hyper::Error>)
.route("/", get(unit));
@ -185,7 +188,8 @@ fn service_handle_on_router_still_impls_routing_dsl() {
#[test]
fn layered() {
let app = route::<_, Body>("/echo", get(unit))
let app = Router::new()
.route::<_, Body>("/echo", get(unit))
.layer(timeout())
.handle_error(handle_error::<BoxError>);
@ -194,7 +198,8 @@ fn layered() {
#[tokio::test] // async because of `.boxed()`
async fn layered_boxed() {
let app = route::<_, Body>("/echo", get(unit))
let app = Router::new()
.route::<_, Body>("/echo", get(unit))
.layer(timeout())
.boxed()
.handle_error(handle_error::<BoxError>);

View file

@ -4,10 +4,8 @@ use crate::{
extract,
handler::{any, delete, get, on, patch, post, Handler},
response::IntoResponse,
route,
routing::nest,
routing::MethodFilter,
service,
service, Router,
};
use bytes::Bytes;
use futures_util::future::Ready;
@ -46,7 +44,9 @@ async fn hello_world() {
"users#create"
}
let app = route("/", get(root).post(foo)).route("/users", post(users_create));
let app = Router::new()
.route("/", get(root).post(foo))
.route("/users", post(users_create));
let addr = run_in_background(app).await;
@ -75,7 +75,7 @@ async fn hello_world() {
#[tokio::test]
async fn consume_body() {
let app = route("/", get(|body: String| async { body }));
let app = Router::new().route("/", get(|body: String| async { body }));
let addr = run_in_background(app).await;
@ -98,7 +98,7 @@ async fn deserialize_body() {
foo: String,
}
let app = route(
let app = Router::new().route(
"/",
post(|input: extract::Json<Input>| async { input.0.foo }),
);
@ -124,7 +124,7 @@ async fn consume_body_to_json_requires_json_content_type() {
foo: String,
}
let app = route(
let app = Router::new().route(
"/",
post(|input: extract::Json<Input>| async { input.0.foo }),
);
@ -156,7 +156,7 @@ async fn body_with_length_limit() {
const LIMIT: u64 = 8;
let app = route(
let app = Router::new().route(
"/",
post(|_body: extract::ContentLengthLimit<Bytes, LIMIT>| async {}),
);
@ -202,16 +202,17 @@ async fn body_with_length_limit() {
#[tokio::test]
async fn routing() {
let app = route(
"/users",
get(|_: Request<Body>| async { "users#index" })
.post(|_: Request<Body>| async { "users#create" }),
)
.route("/users/:id", get(|_: Request<Body>| async { "users#show" }))
.route(
"/users/:id/action",
get(|_: Request<Body>| async { "users#action" }),
);
let app = Router::new()
.route(
"/users",
get(|_: Request<Body>| async { "users#index" })
.post(|_: Request<Body>| async { "users#create" }),
)
.route("/users/:id", get(|_: Request<Body>| async { "users#show" }))
.route(
"/users/:id/action",
get(|_: Request<Body>| async { "users#action" }),
);
let addr = run_in_background(app).await;
@ -255,7 +256,7 @@ async fn routing() {
#[tokio::test]
async fn extracting_url_params() {
let app = route(
let app = Router::new().route(
"/users/:id",
get(|extract::Path(id): extract::Path<i32>| async move {
assert_eq!(id, 42);
@ -288,7 +289,7 @@ async fn extracting_url_params() {
#[tokio::test]
async fn extracting_url_params_multiple_times() {
let app = route(
let app = Router::new().route(
"/users/:id",
get(|_: extract::Path<i32>, _: extract::Path<String>| async {}),
);
@ -307,17 +308,18 @@ async fn extracting_url_params_multiple_times() {
#[tokio::test]
async fn boxing() {
let app = route(
"/",
on(MethodFilter::GET, |_: Request<Body>| async {
"hi from GET"
})
.on(MethodFilter::POST, |_: Request<Body>| async {
"hi from POST"
}),
)
.layer(tower_http::compression::CompressionLayer::new())
.boxed();
let app = Router::new()
.route(
"/",
on(MethodFilter::GET, |_: Request<Body>| async {
"hi from GET"
})
.on(MethodFilter::POST, |_: Request<Body>| async {
"hi from POST"
}),
)
.layer(tower_http::compression::CompressionLayer::new())
.boxed();
let addr = run_in_background(app).await;
@ -345,22 +347,23 @@ async fn routing_between_services() {
"handler"
}
let app = route(
"/one",
service::get(service_fn(|_: Request<Body>| async {
Ok::<_, Infallible>(Response::new(Body::from("one get")))
}))
.post(service_fn(|_: Request<Body>| async {
Ok::<_, Infallible>(Response::new(Body::from("one post")))
}))
.on(
MethodFilter::PUT,
service_fn(|_: Request<Body>| async {
Ok::<_, Infallible>(Response::new(Body::from("one put")))
}),
),
)
.route("/two", service::on(MethodFilter::GET, any(handle)));
let app = Router::new()
.route(
"/one",
service::get(service_fn(|_: Request<Body>| async {
Ok::<_, Infallible>(Response::new(Body::from("one get")))
}))
.post(service_fn(|_: Request<Body>| async {
Ok::<_, Infallible>(Response::new(Body::from("one post")))
}))
.on(
MethodFilter::PUT,
service_fn(|_: Request<Body>| async {
Ok::<_, Infallible>(Response::new(Body::from("one put")))
}),
),
)
.route("/two", service::on(MethodFilter::GET, any(handle)));
let addr = run_in_background(app).await;
@ -408,7 +411,7 @@ async fn middleware_on_single_route() {
"Hello, World!"
}
let app = route(
let app = Router::new().route(
"/",
get(handle.layer(
ServiceBuilder::new()
@ -432,7 +435,7 @@ async fn service_in_bottom() {
Ok(Response::new(hyper::Body::empty()))
}
let app = route("/", service::get(service_fn(handler)));
let app = Router::new().route("/", service::get(service_fn(handler)));
run_in_background(app).await;
}
@ -466,7 +469,7 @@ async fn test_extractor_middleware() {
async fn handler() {}
let app = route(
let app = Router::new().route(
"/",
get(handler.layer(extract::extractor_middleware::<RequireAuth>())),
);
@ -493,7 +496,9 @@ async fn test_extractor_middleware() {
#[tokio::test]
async fn wrong_method_handler() {
let app = route("/", get(|| async {}).post(|| async {})).route("/foo", patch(|| async {}));
let app = Router::new()
.route("/", get(|| async {}).post(|| async {}))
.route("/foo", patch(|| async {}));
let addr = run_in_background(app).await;
@ -547,7 +552,9 @@ async fn wrong_method_service() {
}
}
let app = route("/", service::get(Svc).post(Svc)).route("/foo", service::patch(Svc));
let app = Router::new()
.route("/", service::get(Svc).post(Svc))
.route("/foo", service::patch(Svc));
let addr = run_in_background(app).await;
@ -588,7 +595,7 @@ async fn multiple_methods_for_one_handler() {
"Hello, World!"
}
let app = route("/", on(MethodFilter::GET | MethodFilter::POST, root));
let app = Router::new().route("/", on(MethodFilter::GET | MethodFilter::POST, root));
let addr = run_in_background(app).await;

View file

@ -4,36 +4,39 @@ use std::collections::HashMap;
#[tokio::test]
async fn nesting_apps() {
let api_routes = route(
"/users",
get(|| async { "users#index" }).post(|| async { "users#create" }),
)
.route(
"/users/:id",
get(
|params: extract::Path<HashMap<String, String>>| async move {
format!(
"{}: users#show ({})",
params.get("version").unwrap(),
params.get("id").unwrap()
)
},
),
)
.route(
"/games/:id",
get(
|params: extract::Path<HashMap<String, String>>| async move {
format!(
"{}: games#show ({})",
params.get("version").unwrap(),
params.get("id").unwrap()
)
},
),
);
let api_routes = Router::new()
.route(
"/users",
get(|| async { "users#index" }).post(|| async { "users#create" }),
)
.route(
"/users/:id",
get(
|params: extract::Path<HashMap<String, String>>| async move {
format!(
"{}: users#show ({})",
params.get("version").unwrap(),
params.get("id").unwrap()
)
},
),
)
.route(
"/games/:id",
get(
|params: extract::Path<HashMap<String, String>>| async move {
format!(
"{}: games#show ({})",
params.get("version").unwrap(),
params.get("id").unwrap()
)
},
),
);
let app = route("/", get(|| async { "hi" })).nest("/:version/api", api_routes);
let app = Router::new()
.route("/", get(|| async { "hi" }))
.nest("/:version/api", api_routes);
let addr = run_in_background(app).await;
@ -74,8 +77,8 @@ async fn nesting_apps() {
#[tokio::test]
async fn wrong_method_nest() {
let nested_app = route("/", get(|| async {}));
let app = crate::routing::nest("/", nested_app);
let nested_app = Router::new().route("/", get(|| async {}));
let app = Router::new().nest("/", nested_app);
let addr = run_in_background(app).await;
@ -101,7 +104,7 @@ async fn wrong_method_nest() {
#[tokio::test]
async fn nesting_at_root() {
let app = nest("/", get(|uri: Uri| async move { uri.to_string() }));
let app = Router::new().nest("/", get(|uri: Uri| async move { uri.to_string() }));
let addr = run_in_background(app).await;
@ -130,14 +133,16 @@ async fn nesting_at_root() {
#[tokio::test]
async fn nested_url_extractor() {
let app = nest(
let app = Router::new().nest(
"/foo",
nest(
Router::new().nest(
"/bar",
route("/baz", get(|uri: Uri| async move { uri.to_string() })).route(
"/qux",
get(|req: Request<Body>| async move { req.uri().to_string() }),
),
Router::new()
.route("/baz", get(|uri: Uri| async move { uri.to_string() }))
.route(
"/qux",
get(|req: Request<Body>| async move { req.uri().to_string() }),
),
),
);
@ -164,11 +169,11 @@ async fn nested_url_extractor() {
#[tokio::test]
async fn nested_url_original_extractor() {
let app = nest(
let app = Router::new().nest(
"/foo",
nest(
Router::new().nest(
"/bar",
route(
Router::new().route(
"/baz",
get(|uri: extract::OriginalUri| async move { uri.0.to_string() }),
),
@ -190,11 +195,11 @@ async fn nested_url_original_extractor() {
#[tokio::test]
async fn nested_service_sees_stripped_uri() {
let app = nest(
let app = Router::new().nest(
"/foo",
nest(
Router::new().nest(
"/bar",
route(
Router::new().route(
"/baz",
service_fn(|req: Request<Body>| async move {
let body = box_body(Body::from(req.uri().to_string()));
@ -219,7 +224,7 @@ async fn nested_service_sees_stripped_uri() {
#[tokio::test]
async fn nest_static_file_server() {
let app = nest(
let app = Router::new().nest(
"/static",
service::get(tower_http::services::ServeDir::new(".")).handle_error(|error| {
Ok::<_, Infallible>((

View file

@ -7,8 +7,10 @@ use super::*;
#[tokio::test]
async fn basic() {
let one = route("/foo", get(|| async {})).route("/bar", get(|| async {}));
let two = route("/baz", get(|| async {}));
let one = Router::new()
.route("/foo", get(|| async {}))
.route("/bar", get(|| async {}));
let two = Router::new().route("/baz", get(|| async {}));
let app = one.or(two);
let addr = run_in_background(app).await;
@ -46,10 +48,10 @@ async fn basic() {
#[tokio::test]
async fn multiple_ors_balanced_differently() {
let one = route("/one", get(|| async { "one" }));
let two = route("/two", get(|| async { "two" }));
let three = route("/three", get(|| async { "three" }));
let four = route("/four", get(|| async { "four" }));
let one = Router::new().route("/one", get(|| async { "one" }));
let two = Router::new().route("/two", get(|| async { "two" }));
let three = Router::new().route("/three", get(|| async { "three" }));
let four = Router::new().route("/four", get(|| async { "four" }));
test(
"one",
@ -105,8 +107,10 @@ async fn multiple_ors_balanced_differently() {
#[tokio::test]
async fn or_nested_inside_other_thing() {
let inner = route("/bar", get(|| async {})).or(route("/baz", get(|| async {})));
let app = nest("/foo", inner);
let inner = Router::new()
.route("/bar", get(|| async {}))
.or(Router::new().route("/baz", get(|| async {})));
let app = Router::new().nest("/foo", inner);
let addr = run_in_background(app).await;
@ -129,8 +133,8 @@ async fn or_nested_inside_other_thing() {
#[tokio::test]
async fn or_with_route_following() {
let one = route("/one", get(|| async { "one" }));
let two = route("/two", get(|| async { "two" }));
let one = Router::new().route("/one", get(|| async { "one" }));
let two = Router::new().route("/two", get(|| async { "two" }));
let app = one.or(two).route("/three", get(|| async { "three" }));
let addr = run_in_background(app).await;
@ -161,8 +165,10 @@ async fn or_with_route_following() {
#[tokio::test]
async fn layer() {
let one = route("/foo", get(|| async {}));
let two = route("/bar", get(|| async {})).layer(ConcurrencyLimitLayer::new(10));
let one = Router::new().route("/foo", get(|| async {}));
let two = Router::new()
.route("/bar", get(|| async {}))
.layer(ConcurrencyLimitLayer::new(10));
let app = one.or(two);
let addr = run_in_background(app).await;
@ -186,8 +192,9 @@ async fn layer() {
#[tokio::test]
async fn layer_and_handle_error() {
let one = route("/foo", get(|| async {}));
let two = route("/time-out", get(futures::future::pending::<()>))
let one = Router::new().route("/foo", get(|| async {}));
let two = Router::new()
.route("/time-out", get(futures::future::pending::<()>))
.layer(TimeoutLayer::new(Duration::from_millis(10)))
.handle_error(|_| Ok(StatusCode::REQUEST_TIMEOUT));
let app = one.or(two);
@ -206,8 +213,8 @@ async fn layer_and_handle_error() {
#[tokio::test]
async fn nesting() {
let one = route("/foo", get(|| async {}));
let two = nest("/bar", route("/baz", get(|| async {})));
let one = Router::new().route("/foo", get(|| async {}));
let two = Router::new().nest("/bar", Router::new().route("/baz", get(|| async {})));
let app = one.or(two);
let addr = run_in_background(app).await;
@ -224,8 +231,8 @@ async fn nesting() {
#[tokio::test]
async fn boxed() {
let one = route("/foo", get(|| async {})).boxed();
let two = route("/bar", get(|| async {})).boxed();
let one = Router::new().route("/foo", get(|| async {})).boxed();
let two = Router::new().route("/bar", get(|| async {})).boxed();
let app = one.or(two);
let addr = run_in_background(app).await;
@ -242,13 +249,14 @@ async fn boxed() {
#[tokio::test]
async fn many_ors() {
let app = route("/r1", get(|| async {}))
.or(route("/r2", get(|| async {})))
.or(route("/r3", get(|| async {})))
.or(route("/r4", get(|| async {})))
.or(route("/r5", get(|| async {})))
.or(route("/r6", get(|| async {})))
.or(route("/r7", get(|| async {})));
let app = Router::new()
.route("/r1", get(|| async {}))
.or(Router::new().route("/r2", get(|| async {})))
.or(Router::new().route("/r3", get(|| async {})))
.or(Router::new().route("/r4", get(|| async {})))
.or(Router::new().route("/r5", get(|| async {})))
.or(Router::new().route("/r6", get(|| async {})))
.or(Router::new().route("/r7", get(|| async {})));
let addr = run_in_background(app).await;
@ -273,18 +281,19 @@ async fn many_ors() {
#[tokio::test]
async fn services() {
let app = route(
"/foo",
crate::service::get(service_fn(|_: Request<Body>| async {
Ok::<_, Infallible>(Response::new(Body::empty()))
})),
)
.or(route(
"/bar",
crate::service::get(service_fn(|_: Request<Body>| async {
Ok::<_, Infallible>(Response::new(Body::empty()))
})),
));
let app = Router::new()
.route(
"/foo",
crate::service::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 {
Ok::<_, Infallible>(Response::new(Body::empty()))
})),
));
let addr = run_in_background(app).await;
@ -319,8 +328,8 @@ async fn all_the_uris(
#[tokio::test]
async fn nesting_and_seeing_the_right_uri() {
let one = nest("/foo", route("/bar", get(all_the_uris)));
let two = route("/foo", get(all_the_uris));
let one = Router::new().nest("/foo", Router::new().route("/bar", get(all_the_uris)));
let two = Router::new().route("/foo", get(all_the_uris));
let addr = run_in_background(one.or(two)).await;
@ -359,8 +368,11 @@ async fn nesting_and_seeing_the_right_uri() {
#[tokio::test]
async fn nesting_and_seeing_the_right_uri_at_more_levels_of_nesting() {
let one = nest("/foo", nest("/bar", route("/baz", get(all_the_uris))));
let two = route("/foo", get(all_the_uris));
let one = Router::new().nest(
"/foo",
Router::new().nest("/bar", Router::new().route("/baz", get(all_the_uris))),
);
let two = Router::new().route("/foo", get(all_the_uris));
let addr = run_in_background(one.or(two)).await;
@ -399,9 +411,12 @@ async fn nesting_and_seeing_the_right_uri_at_more_levels_of_nesting() {
#[tokio::test]
async fn nesting_and_seeing_the_right_uri_ors_with_nesting() {
let one = nest("/foo", nest("/bar", route("/baz", get(all_the_uris))));
let two = nest("/foo", route("/qux", get(all_the_uris)));
let three = route("/foo", get(all_the_uris));
let one = Router::new().nest(
"/foo",
Router::new().nest("/bar", Router::new().route("/baz", get(all_the_uris))),
);
let two = Router::new().nest("/foo", Router::new().route("/qux", get(all_the_uris)));
let three = Router::new().route("/foo", get(all_the_uris));
let addr = run_in_background(one.or(two).or(three)).await;
@ -455,8 +470,11 @@ async fn nesting_and_seeing_the_right_uri_ors_with_nesting() {
#[tokio::test]
async fn nesting_and_seeing_the_right_uri_ors_with_multi_segment_uris() {
let one = nest("/foo", nest("/bar", route("/baz", get(all_the_uris))));
let two = route("/foo/bar", get(all_the_uris));
let one = Router::new().nest(
"/foo",
Router::new().nest("/bar", Router::new().route("/baz", get(all_the_uris))),
);
let two = Router::new().route("/foo/bar", get(all_the_uris));
let addr = run_in_background(one.or(two)).await;