Skip to content

Commit 1634e67

Browse files
authored
Stores routes in a map (#408)
With #404 and #402 all routes now have the same types and thus we don't need to nest them but can instead store them all in a map. This simplifies the routing quite a bit and is faster as well. High level changes: - Routes are now stored in a `HashMap<RouteId, Route<B>>`. - `Router::or` is renamed to `Router::merge` because thats what it does now. It copies all routes from one router to another. This also means overlapping routes will cause a panic which is nice win. - `Router::merge` now only accepts `Router`s so added `Router::fallback` for adding a global 404 handler. - The `Or` service has been removed. - `Router::layer` now only adds layers to the routes you actually have meaning middleware runs _after_ routing. I believe that addresses #380 but will test that on another branch.
1 parent fb87a6a commit 1634e67

File tree

8 files changed

+569
-538
lines changed

8 files changed

+569
-538
lines changed

examples/global-404-handler/src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ async fn main() {
2424
// build our application with a route
2525
let app = Router::new().route("/", get(handler));
2626

27-
// make sure this is added as the very last thing
28-
let app = app.or(handler_404.into_service());
27+
// add a fallback service for handling routes to unknown paths
28+
let app = app.fallback(handler_404.into_service());
2929

3030
// run it
3131
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));

src/lib.rs

Lines changed: 122 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@
77
//! - [Handlers](#handlers)
88
//! - [Debugging handler type errors](#debugging-handler-type-errors)
99
//! - [Routing](#routing)
10-
//! - [Routing to any `Service`](#routing-to-any-service)
11-
//! - [Routing to fallible services](#routing-to-fallible-services)
1210
//! - [Wildcard routes](#wildcard-routes)
1311
//! - [Nesting routes](#nesting-routes)
12+
//! - [Fallback routes](#fallback-routes)
13+
//! - [Routing to any `Service`](#routing-to-any-service)
14+
//! - [Routing to fallible services](#routing-to-fallible-services)
1415
//! - [Extractors](#extractors)
1516
//! - [Common extractors](#common-extractors)
1617
//! - [Applying multiple extractors](#applying-multiple-extractors)
@@ -143,7 +144,7 @@
143144
//!
144145
//! # Routing
145146
//!
146-
//! Routing between handlers looks like this:
147+
//! [`Router::route`] is the main way to add routes:
147148
//!
148149
//! ```rust,no_run
149150
//! use axum::{
@@ -174,11 +175,125 @@
174175
//! Routes can also be dynamic like `/users/:id`. See [extractors](#extractors)
175176
//! for more details.
176177
//!
177-
//! You can also define routes separately and merge them with [`Router::or`].
178+
//! You can also define routes separately and merge them with [`Router::merge`].
178179
//!
179180
//! Routes are not allowed to overlap and will panic if an overlapping route is
180181
//! added. This also means the order in which routes are added doesn't matter.
181182
//!
183+
//! ## Wildcard routes
184+
//!
185+
//! axum also supports wildcard routes:
186+
//!
187+
//! ```rust,no_run
188+
//! use axum::{
189+
//! routing::get,
190+
//! Router,
191+
//! };
192+
//!
193+
//! let app = Router::new()
194+
//! // this matches any request that starts with `/api`
195+
//! .route("/api/*rest", get(|| async { /* ... */ }));
196+
//! # async {
197+
//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
198+
//! # };
199+
//! ```
200+
//!
201+
//! The matched path can be extracted via [`extract::Path`]:
202+
//!
203+
//! ```rust,no_run
204+
//! use axum::{
205+
//! routing::get,
206+
//! extract::Path,
207+
//! Router,
208+
//! };
209+
//!
210+
//! let app = Router::new().route("/api/*rest", get(|Path(rest): Path<String>| async {
211+
//! // `rest` will be everything after `/api`
212+
//! }));
213+
//! # async {
214+
//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
215+
//! # };
216+
//! ```
217+
//!
218+
//! ## Nesting routes
219+
//!
220+
//! Routes can be nested by calling [`Router::nest`](routing::Router::nest):
221+
//!
222+
//! ```rust,no_run
223+
//! use axum::{
224+
//! body::{Body, BoxBody},
225+
//! http::Request,
226+
//! routing::get,
227+
//! Router,
228+
//! };
229+
//! use tower_http::services::ServeFile;
230+
//! use http::Response;
231+
//!
232+
//! fn api_routes() -> Router {
233+
//! Router::new()
234+
//! .route("/users", get(|_: Request<Body>| async { /* ... */ }))
235+
//! }
236+
//!
237+
//! let app = Router::new()
238+
//! .route("/", get(|_: Request<Body>| async { /* ... */ }))
239+
//! .nest("/api", api_routes());
240+
//! # async {
241+
//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
242+
//! # };
243+
//! ```
244+
//!
245+
//! Note that nested routes will not see the orignal request URI but instead
246+
//! have the matched prefix stripped. This is necessary for services like static
247+
//! file serving to work. Use [`OriginalUri`] if you need the original request
248+
//! URI.
249+
//!
250+
//! Nested routes are similar to wild card routes. The difference is that
251+
//! wildcard routes still see the whole URI whereas nested routes will have
252+
//! the prefix stripped.
253+
//!
254+
//! ```rust
255+
//! use axum::{routing::get, http::Uri, Router};
256+
//!
257+
//! let app = Router::new()
258+
//! .route("/foo/*rest", get(|uri: Uri| async {
259+
//! // `uri` will contain `/foo`
260+
//! }))
261+
//! .nest("/bar", get(|uri: Uri| async {
262+
//! // `uri` will _not_ contain `/bar`
263+
//! }));
264+
//! # async {
265+
//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
266+
//! # };
267+
//! ```
268+
//!
269+
//! ## Fallback routes
270+
//!
271+
//! By default axum will respond with an empty `404 Not Found` response to unhandled requests. To
272+
//! override that you can use [`Router::fallback`]:
273+
//!
274+
//! ```rust
275+
//! use axum::{
276+
//! Router,
277+
//! routing::get,
278+
//! handler::Handler,
279+
//! response::IntoResponse,
280+
//! http::{StatusCode, Uri},
281+
//! };
282+
//!
283+
//! async fn fallback(uri: Uri) -> impl IntoResponse {
284+
//! (StatusCode::NOT_FOUND, format!("No route for {}", uri))
285+
//! }
286+
//!
287+
//! let app = Router::new()
288+
//! .route("/foo", get(|| async { /* ... */ }))
289+
//! .fallback(fallback.into_service());
290+
//! # async {
291+
//! # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
292+
//! # };
293+
//! ```
294+
//!
295+
//! See [`Router::fallback`] for more details.
296+
//!
182297
//! ## Routing to any [`Service`]
183298
//!
184299
//! axum also supports routing to general [`Service`]s:
@@ -314,92 +429,6 @@
314429
//! See ["Error handling"](#error-handling) for more details on [`handle_error`]
315430
//! and error handling in general.
316431
//!
317-
//! ## Wildcard routes
318-
//!
319-
//! axum also supports wildcard routes:
320-
//!
321-
//! ```rust,no_run
322-
//! use axum::{
323-
//! routing::get,
324-
//! Router,
325-
//! };
326-
//!
327-
//! let app = Router::new()
328-
//! // this matches any request that starts with `/api`
329-
//! .route("/api/*rest", get(|| async { /* ... */ }));
330-
//! # async {
331-
//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
332-
//! # };
333-
//! ```
334-
//!
335-
//! The matched path can be extracted via [`extract::Path`]:
336-
//!
337-
//! ```rust,no_run
338-
//! use axum::{
339-
//! routing::get,
340-
//! extract::Path,
341-
//! Router,
342-
//! };
343-
//!
344-
//! let app = Router::new().route("/api/*rest", get(|Path(rest): Path<String>| async {
345-
//! // `rest` will be everything after `/api`
346-
//! }));
347-
//! # async {
348-
//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
349-
//! # };
350-
//! ```
351-
//!
352-
//! ## Nesting routes
353-
//!
354-
//! Routes can be nested by calling [`Router::nest`](routing::Router::nest):
355-
//!
356-
//! ```rust,no_run
357-
//! use axum::{
358-
//! body::{Body, BoxBody},
359-
//! http::Request,
360-
//! routing::get,
361-
//! Router,
362-
//! };
363-
//! use tower_http::services::ServeFile;
364-
//! use http::Response;
365-
//!
366-
//! fn api_routes() -> Router {
367-
//! Router::new()
368-
//! .route("/users", get(|_: Request<Body>| async { /* ... */ }))
369-
//! }
370-
//!
371-
//! let app = Router::new()
372-
//! .route("/", get(|_: Request<Body>| async { /* ... */ }))
373-
//! .nest("/api", api_routes());
374-
//! # async {
375-
//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
376-
//! # };
377-
//! ```
378-
//!
379-
//! Note that nested routes will not see the orignal request URI but instead
380-
//! have the matched prefix stripped. This is necessary for services like static
381-
//! file serving to work. Use [`OriginalUri`] if you need the original request
382-
//! URI.
383-
//!
384-
//! Nested routes are similar to wild card routes. The difference is that
385-
//! wildcard routes still see the whole URI whereas nested routes will have
386-
//! the prefix stripped.
387-
//!
388-
//! ```rust
389-
//! use axum::{routing::get, http::Uri, Router};
390-
//!
391-
//! let app = Router::new()
392-
//! .route("/foo/*rest", get(|uri: Uri| async {
393-
//! // `uri` will contain `/foo`
394-
//! }))
395-
//! .nest("/bar", get(|uri: Uri| async {
396-
//! // `uri` will _not_ contain `/bar`
397-
//! }));
398-
//! # async {
399-
//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
400-
//! # };
401-
//! ```
402-
//!
403432
//! # Extractors
404433
//!
405434
//! An extractor is a type that implements [`FromRequest`]. Extractors is how
@@ -862,7 +891,7 @@
862891
//! Note that [`Router::layer`] applies the middleware to all previously added
863892
//! routes, of that particular `Router`. If you need multiple groups of routes
864893
//! with different middleware build them separately and combine them with
865-
//! [`Router::or`]:
894+
//! [`Router::merge`]:
866895
//!
867896
//! ```rust,no_run
868897
//! use axum::{
@@ -883,7 +912,7 @@
883912
//! .route("/requires-auth", get(handler))
884913
//! .layer(MyAuthLayer::new());
885914
//!
886-
//! let app = foo.or(bar);
915+
//! let app = foo.merge(bar);
887916
//! # async {
888917
//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
889918
//! # };
@@ -1148,7 +1177,7 @@
11481177
//! [`IntoResponse`]: crate::response::IntoResponse
11491178
//! [`Timeout`]: tower::timeout::Timeout
11501179
//! [examples]: https://github.com/tokio-rs/axum/tree/main/examples
1151-
//! [`Router::or`]: crate::routing::Router::or
1180+
//! [`Router::merge`]: crate::routing::Router::merge
11521181
//! [`axum::Server`]: hyper::server::Server
11531182
//! [`OriginalUri`]: crate::extract::OriginalUri
11541183
//! [`Service`]: tower::Service

0 commit comments

Comments
 (0)