From 6f30d4aa6a7280a843b25eefd908c457bfb31ea9 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 1 Aug 2021 15:42:50 +0200 Subject: [PATCH] Improve documentation for router (#71) Fixes #67 --- CHANGELOG.md | 1 + src/lib.rs | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 89 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 122d2048..92a40d68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Implement `Deref` most extractors ([#56](https://github.com/tokio-rs/axum/pull/56)) - Return `405 Method Not Allowed` for unsupported method for route ([#63](https://github.com/tokio-rs/axum/pull/63)) - Add extractor for remote connection info ([#55](https://github.com/tokio-rs/axum/pull/55)) +- Improve documentation for routing ([#71](https://github.com/tokio-rs/axum/pull/71)) - Clarify required response body type when routing to `tower::Service`s ([#69](https://github.com/tokio-rs/axum/pull/69)) - Add `axum::body::box_body` to converting an `http_body::Body` to `axum::body::BoxBody` ([#69](https://github.com/tokio-rs/axum/pull/69)) diff --git a/src/lib.rs b/src/lib.rs index b4c452e5..64853aa3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,8 @@ //! - [Compatibility](#compatibility) //! - [Handlers](#handlers) //! - [Routing](#routing) +//! - [Precedence](#precedence) +//! - [Matching multiple methods](#matching-multiple-methods) //! - [Extractors](#extractors) //! - [Building responses](#building-responses) //! - [Applying middleware](#applying-middleware) @@ -122,7 +124,92 @@ //! # }; //! ``` //! -//! Routes can also be dynamic like `/users/:id`. +//! Routes can also be dynamic like `/users/:id`. See [extractors](#extractors) +//! for more details. +//! +//! ## Precedence +//! +//! Note that routes are matched _bottom to top_ so routes that should have +//! higher precedence should be added _after_ routes with lower precedence: +//! +//! ```rust +//! use axum::{prelude::*, body::BoxBody}; +//! use tower::{Service, ServiceExt, BoxError}; +//! use http::{Method, Response, StatusCode}; +//! use std::convert::Infallible; +//! +//! # #[tokio::main] +//! # 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" })) +//! .route("/:key", get(|| async { "/:key called" })); +//! +//! // Even though we use `/foo` as the request URI, `/:key` takes precedence +//! // since its defined last. +//! let (status, body) = call_service(&mut app, Method::GET, "/foo").await; +//! assert_eq!(status, StatusCode::OK); +//! assert_eq!(body, "/:key called"); +//! +//! // We have to add `/foo` after `/:key` since routes are matched bottom to +//! // top. +//! let mut new_app = route("/:key", get(|| async { "/:key called" })) +//! .route("/foo", get(|| async { "/foo called" })); +//! +//! // Now it works +//! let (status, body) = call_service(&mut new_app, Method::GET, "/foo").await; +//! assert_eq!(status, StatusCode::OK); +//! assert_eq!(body, "/foo called"); +//! +//! // And the other route works as well +//! let (status, body) = call_service(&mut new_app, Method::GET, "/bar").await; +//! assert_eq!(status, StatusCode::OK); +//! assert_eq!(body, "/:key called"); +//! +//! // Little helper function to make calling a service easier. Just for +//! // demonstration purposes. +//! async fn call_service( +//! svc: &mut S, +//! method: Method, +//! uri: &str, +//! ) -> (StatusCode, String) +//! where +//! S: Service, Response = Response, Error = Infallible> +//! { +//! let req = Request::builder().method(method).uri(uri).body(Body::empty()).unwrap(); +//! let res = svc.ready().await.unwrap().call(req).await.unwrap(); +//! +//! let status = res.status(); +//! +//! let body = res.into_body(); +//! let body = hyper::body::to_bytes(body).await.unwrap(); +//! let body = String::from_utf8(body.to_vec()).unwrap(); +//! +//! (status, body) +//! } +//! # } +//! ``` +//! +//! ## Matching multiple methods +//! +//! If you want a path to accept multiple HTTP methods you must add them all at +//! once: +//! +//! ```rust,no_run +//! use axum::prelude::*; +//! +//! // `GET /` and `POST /` are both accepted +//! let app = route("/", get(handler).post(handler)); +//! +//! // This will _not_ work. Only `POST /` will be accessible. +//! let wont_work = route("/", get(handler)).route("/", post(handler)); +//! +//! async fn handler() {} +//! # async { +//! # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); +//! # hyper::Server::bind(&"".parse().unwrap()).serve(wont_work.into_make_service()).await.unwrap(); +//! # }; +//! ``` //! //! # Extractors //!