diff --git a/axum/src/docs/debugging_handler_type_errors.md b/axum/src/docs/debugging_handler_type_errors.md deleted file mode 100644 index 731a6a95..00000000 --- a/axum/src/docs/debugging_handler_type_errors.md +++ /dev/null @@ -1,39 +0,0 @@ -## Debugging handler type errors - -For a function to be used as a handler it must implement the [`Handler`] trait. -axum provides blanket implementations for functions that: - -- Are `async fn`s. -- Take no more than 16 arguments that all implement `Send`. - - All except the last argument implement [`FromRequestParts`]. - - The last argument implements [`FromRequest`]. -- Returns something that implements [`IntoResponse`]. -- If a closure is used it must implement `Clone + Send` and be -`'static`. -- Returns a future that is `Send`. The most common way to accidentally make a -future `!Send` is to hold a `!Send` type across an await. - -Unfortunately Rust gives poor error messages if you try to use a function -that doesn't quite match what's required by [`Handler`]. - -You might get an error like this: - -```not_rust -error[E0277]: the trait bound `fn(bool) -> impl Future {handler}: Handler<_, _>` is not satisfied - --> src/main.rs:13:44 - | -13 | let app = Router::new().route("/", get(handler)); - | ^^^^^^^ the trait `Handler<_, _>` is not implemented for `fn(bool) -> impl Future {handler}` - | - ::: axum/src/handler/mod.rs:116:8 - | -116 | H: Handler, - | ------------- required by this bound in `axum::routing::get` -``` - -This error doesn't tell you _why_ your function doesn't implement -[`Handler`]. It's possible to improve the error with the [`debug_handler`] -proc-macro from the [axum-macros] crate. - -[axum-macros]: https://docs.rs/axum-macros -[`debug_handler`]: https://docs.rs/axum-macros/latest/axum_macros/attr.debug_handler.html diff --git a/axum/src/docs/error_handling.md b/axum/src/docs/error_handling.md deleted file mode 100644 index 7d7e14ee..00000000 --- a/axum/src/docs/error_handling.md +++ /dev/null @@ -1,179 +0,0 @@ -Error handling model and utilities - -# axum's error handling model - -axum is based on [`tower::Service`] which bundles errors through its associated -`Error` type. If you have a [`Service`] that produces an error and that error -makes it all the way up to hyper, the connection will be terminated _without_ -sending a response. This is generally not desirable so axum makes sure you -always produce a response by relying on the type system. - -axum does this by requiring all services have [`Infallible`] as their error -type. `Infallible` is the error type for errors that can never happen. - -This means if you define a handler like: - -```rust -use axum::http::StatusCode; - -async fn handler() -> Result { - # todo!() - // ... -} -``` - -While it looks like it might fail with a `StatusCode` this actually isn't an -"error". If this handler returns `Err(some_status_code)` that will still be -converted into a [`Response`] and sent back to the client. This is done -through `StatusCode`'s [`IntoResponse`] implementation. - -It doesn't matter whether you return `Err(StatusCode::NOT_FOUND)` or -`Err(StatusCode::INTERNAL_SERVER_ERROR)`. These are not considered errors in -axum. - -Instead of a direct `StatusCode`, it makes sense to use intermediate error type -that can ultimately be converted to `Response`. This allows using `?` operator -in handlers. See those examples: - -* [`anyhow-error-response`][anyhow] for generic boxed errors -* [`error-handling`][error-handling] for application-specific detailed errors - -[anyhow]: https://github.com/tokio-rs/axum/blob/main/examples/anyhow-error-response/src/main.rs -[error-handling]: https://github.com/tokio-rs/axum/blob/main/examples/error-handling/src/main.rs - -This also applies to extractors. If an extractor doesn't match the request the -request will be rejected and a response will be returned without calling your -handler. See [`extract`](crate::extract) to learn more about handling extractor -failures. - -# Routing to fallible services - -You generally don't have to think about errors if you're only using async -functions as handlers. However if you're embedding general `Service`s or -applying middleware, which might produce errors you have to tell axum how to -convert those errors into responses. - -```rust -use axum::{ - Router, - body::Body, - http::{Request, Response, StatusCode}, - error_handling::HandleError, -}; - -async fn thing_that_might_fail() -> Result<(), anyhow::Error> { - # Ok(()) - // ... -} - -// this service might fail with `anyhow::Error` -let some_fallible_service = tower::service_fn(|_req| async { - thing_that_might_fail().await?; - Ok::<_, anyhow::Error>(Response::new(Body::empty())) -}); - -let app = Router::new().route_service( - "/", - // we cannot route to `some_fallible_service` directly since it might fail. - // we have to use `handle_error` which converts its errors into responses - // and changes its error type from `anyhow::Error` to `Infallible`. - HandleError::new(some_fallible_service, handle_anyhow_error), -); - -// handle errors by converting them into something that implements -// `IntoResponse` -async fn handle_anyhow_error(err: anyhow::Error) -> (StatusCode, String) { - ( - StatusCode::INTERNAL_SERVER_ERROR, - format!("Something went wrong: {err}"), - ) -} -# let _: Router = app; -``` - -# Applying fallible middleware - -Similarly axum requires you to handle errors from middleware. That is done with -[`HandleErrorLayer`]: - -```rust -use axum::{ - Router, - BoxError, - routing::get, - http::StatusCode, - error_handling::HandleErrorLayer, -}; -use std::time::Duration; -use tower::ServiceBuilder; - -let app = Router::new() - .route("/", get(|| async {})) - .layer( - ServiceBuilder::new() - // `timeout` will produce an error if the handler takes - // too long so we must handle those - .layer(HandleErrorLayer::new(handle_timeout_error)) - .timeout(Duration::from_secs(30)) - ); - -async fn handle_timeout_error(err: BoxError) -> (StatusCode, String) { - if err.is::() { - ( - StatusCode::REQUEST_TIMEOUT, - "Request took too long".to_string(), - ) - } else { - ( - StatusCode::INTERNAL_SERVER_ERROR, - format!("Unhandled internal error: {err}"), - ) - } -} -# let _: Router = app; -``` - -# Running extractors for error handling - -`HandleErrorLayer` also supports running extractors: - -```rust -use axum::{ - Router, - BoxError, - routing::get, - http::{StatusCode, Method, Uri}, - error_handling::HandleErrorLayer, -}; -use std::time::Duration; -use tower::ServiceBuilder; - -let app = Router::new() - .route("/", get(|| async {})) - .layer( - ServiceBuilder::new() - // `timeout` will produce an error if the handler takes - // too long so we must handle those - .layer(HandleErrorLayer::new(handle_timeout_error)) - .timeout(Duration::from_secs(30)) - ); - -async fn handle_timeout_error( - // `Method` and `Uri` are extractors so they can be used here - method: Method, - uri: Uri, - // the last argument must be the error itself - err: BoxError, -) -> (StatusCode, String) { - ( - StatusCode::INTERNAL_SERVER_ERROR, - format!("`{method} {uri}` failed with {err}"), - ) -} -# let _: Router = app; -``` - -[`tower::Service`]: `tower::Service` -[`Infallible`]: std::convert::Infallible -[`Response`]: crate::response::Response -[`IntoResponse`]: crate::response::IntoResponse diff --git a/axum/src/docs/extract.md b/axum/src/docs/extract.md deleted file mode 100644 index 4659d908..00000000 --- a/axum/src/docs/extract.md +++ /dev/null @@ -1,689 +0,0 @@ -Types and traits for extracting data from requests. - -# Intro - -A handler function is an async function that takes any number of -"extractors" as arguments. An extractor is a type that implements -[`FromRequest`] or [`FromRequestParts`]. - -For example, [`Json`] is an extractor that consumes the request body and -deserializes it as JSON into some target type: - -```rust,no_run -use axum::{ - extract::Json, - routing::post, - handler::Handler, - Router, -}; -use serde::Deserialize; - -#[derive(Deserialize)] -struct CreateUser { - email: String, - password: String, -} - -async fn create_user(Json(payload): Json) { - // ... -} - -let app = Router::new().route("/users", post(create_user)); -# let _: Router = app; -``` - -# Common extractors - -Some commonly used extractors are: - -```rust,no_run -use axum::{ - extract::{Request, Json, Path, Extension, Query}, - routing::post, - http::header::HeaderMap, - body::{Bytes, Body}, - Router, -}; -use serde_json::Value; -use std::collections::HashMap; - -// `Path` gives you the path parameters and deserializes them. See its docs for -// more details -async fn path(Path(user_id): Path) {} - -// `Query` gives you the query parameters and deserializes them. -async fn query(Query(params): Query>) {} - -// `HeaderMap` gives you all the headers -async fn headers(headers: HeaderMap) {} - -// `String` consumes the request body and ensures it is valid utf-8 -async fn string(body: String) {} - -// `Bytes` gives you the raw request body -async fn bytes(body: Bytes) {} - -// We've already seen `Json` for parsing the request body as json -async fn json(Json(payload): Json) {} - -// `Request` gives you the whole request for maximum control -async fn request(request: Request) {} - -// `Extension` extracts data from "request extensions" -// This is commonly used to share state with handlers -async fn extension(Extension(state): Extension) {} - -#[derive(Clone)] -struct State { /* ... */ } - -let app = Router::new() - .route("/path/{user_id}", post(path)) - .route("/query", post(query)) - .route("/string", post(string)) - .route("/bytes", post(bytes)) - .route("/json", post(json)) - .route("/request", post(request)) - .route("/extension", post(extension)); -# let _: Router = app; -``` - -# Applying multiple extractors - -You can also apply multiple extractors: - -```rust,no_run -use axum::{ - extract::{Path, Query}, - routing::get, - Router, -}; -use uuid::Uuid; -use serde::Deserialize; - -let app = Router::new().route("/users/{id}/things", get(get_user_things)); - -#[derive(Deserialize)] -struct Pagination { - page: usize, - per_page: usize, -} - -async fn get_user_things( - Path(user_id): Path, - Query(pagination): Query, -) { - // ... -} -# let _: Router = app; -``` - -# The order of extractors - -Extractors always run in the order of the function parameters that is from -left to right. - -The request body is an asynchronous stream that can only be consumed once. -Therefore you can only have one extractor that consumes the request body. axum -enforces this by requiring such extractors to be the _last_ argument your -handler takes. - -For example - -```rust -use axum::{extract::State, http::{Method, HeaderMap}}; -# -# #[derive(Clone)] -# struct AppState { -# } - -async fn handler( - // `Method` and `HeaderMap` don't consume the request body so they can - // put anywhere in the argument list (but before `body`) - method: Method, - headers: HeaderMap, - // `State` is also an extractor so it needs to be before `body` - State(state): State, - // `String` consumes the request body and thus must be the last extractor - body: String, -) { - // ... -} -# -# let _: axum::routing::MethodRouter = axum::routing::get(handler); -``` - -We get a compile error if `String` isn't the last extractor: - -```rust,compile_fail -use axum::http::Method; - -async fn handler( - // this doesn't work since `String` must be the last argument - body: String, - method: Method, -) { - // ... -} -# -# let _: axum::routing::MethodRouter = axum::routing::get(handler); -``` - -This also means you cannot consume the request body twice: - -```rust,compile_fail -use axum::Json; -use serde::Deserialize; - -#[derive(Deserialize)] -struct Payload {} - -async fn handler( - // `String` and `Json` both consume the request body - // so they cannot both be used - string_body: String, - json_body: Json, -) { - // ... -} -# -# let _: axum::routing::MethodRouter = axum::routing::get(handler); -``` - -axum enforces this by requiring the last extractor implements [`FromRequest`] -and all others implement [`FromRequestParts`]. - -# Handling extractor rejections - -If you want to handle the case of an extractor failing within a specific -handler, you can wrap it in `Result`, with the error being the rejection type -of the extractor: - -```rust,no_run -use axum::{ - extract::{Json, rejection::JsonRejection}, - routing::post, - Router, -}; -use serde_json::Value; - -async fn create_user(payload: Result, JsonRejection>) { - match payload { - Ok(payload) => { - // We got a valid JSON payload - } - Err(JsonRejection::MissingJsonContentType(_)) => { - // Request didn't have `Content-Type: application/json` - // header - } - Err(JsonRejection::JsonDataError(_)) => { - // Couldn't deserialize the body into the target type - } - Err(JsonRejection::JsonSyntaxError(_)) => { - // Syntax error in the body - } - Err(JsonRejection::BytesRejection(_)) => { - // Failed to extract the request body - } - Err(_) => { - // `JsonRejection` is marked `#[non_exhaustive]` so match must - // include a catch-all case. - } - } -} - -let app = Router::new().route("/users", post(create_user)); -# let _: Router = app; -``` - -# Optional extractors - -Some extractors implement [`OptionalFromRequestParts`] in addition to -[`FromRequestParts`], or [`OptionalFromRequest`] in addition to [`FromRequest`]. - -These extractors can be used inside of `Option`. It depends on the particular -`OptionalFromRequestParts` or `OptionalFromRequest` implementation what this -does: For example for `TypedHeader` from axum-extra, you get `None` if the -header you're trying to extract is not part of the request, but if the header -is present and fails to parse, the request is rejected. - -```rust,no_run -use axum::{routing::post, Router}; -use axum_extra::{headers::UserAgent, TypedHeader}; -use serde_json::Value; - -async fn foo(user_agent: Option>) { - if let Some(TypedHeader(user_agent)) = user_agent { - // The client sent a user agent - } else { - // No user agent header - } -} - -let app = Router::new().route("/foo", post(foo)); -# let _: Router = app; -``` - -# Customizing extractor responses - -If an extractor fails it will return a response with the error and your -handler will not be called. To customize the error response you have two -options: - -1. Use `Result` as your extractor like shown in - ["Handling extractor rejections"](#handling-extractor-rejections). - This works well if you're only using the extractor in a single handler. -2. Create your own extractor that in its [`FromRequest`] implementation calls - one of axum's built in extractors but returns a different response for - rejections. See the [customize-extractor-error] example for more details. - -# Accessing inner errors - -axum's built-in extractors don't directly expose the inner error. This gives us -more flexibility and allows us to change internal implementations without -breaking the public API. - -For example that means while [`Json`] is implemented using [`serde_json`] it -doesn't directly expose the [`serde_json::Error`] that's contained in -[`JsonRejection::JsonDataError`]. However it is still possible to access via -methods from [`std::error::Error`]: - -```rust -use std::error::Error; -use axum::{ - extract::{Json, rejection::JsonRejection}, - response::IntoResponse, - http::StatusCode, -}; -use serde_json::{json, Value}; - -async fn handler( - result: Result, JsonRejection>, -) -> Result, (StatusCode, String)> { - match result { - // if the client sent valid JSON then we're good - Ok(Json(payload)) => Ok(Json(json!({ "payload": payload }))), - - Err(err) => match err { - JsonRejection::JsonDataError(err) => { - Err(serde_json_error_response(err)) - } - JsonRejection::JsonSyntaxError(err) => { - Err(serde_json_error_response(err)) - } - // handle other rejections from the `Json` extractor - JsonRejection::MissingJsonContentType(_) => Err(( - StatusCode::BAD_REQUEST, - "Missing `Content-Type: application/json` header".to_string(), - )), - JsonRejection::BytesRejection(_) => Err(( - StatusCode::INTERNAL_SERVER_ERROR, - "Failed to buffer request body".to_string(), - )), - // we must provide a catch-all case since `JsonRejection` is marked - // `#[non_exhaustive]` - _ => Err(( - StatusCode::INTERNAL_SERVER_ERROR, - "Unknown error".to_string(), - )), - }, - } -} - -// attempt to extract the inner `serde_path_to_error::Error`, -// if that succeeds we can provide a more specific error. -// -// `Json` uses `serde_path_to_error` so the error will be wrapped in `serde_path_to_error::Error`. -fn serde_json_error_response(err: E) -> (StatusCode, String) -where - E: Error + 'static, -{ - if let Some(err) = find_error_source::>(&err) { - let serde_json_err = err.inner(); - ( - StatusCode::BAD_REQUEST, - format!( - "Invalid JSON at line {} column {}", - serde_json_err.line(), - serde_json_err.column() - ), - ) - } else { - (StatusCode::BAD_REQUEST, "Unknown error".to_string()) - } -} - -// attempt to downcast `err` into a `T` and if that fails recursively try and -// downcast `err`'s source -fn find_error_source<'a, T>(err: &'a (dyn Error + 'static)) -> Option<&'a T> -where - T: Error + 'static, -{ - if let Some(err) = err.downcast_ref::() { - Some(err) - } else if let Some(source) = err.source() { - find_error_source(source) - } else { - None - } -} -# -# #[tokio::main] -# async fn main() { -# use axum::extract::FromRequest; -# -# let req = axum::http::Request::builder() -# .header("content-type", "application/json") -# .body(axum::body::Body::from("{")) -# .unwrap(); -# -# let err = match Json::::from_request(req, &()).await.unwrap_err() { -# JsonRejection::JsonSyntaxError(err) => err, -# _ => panic!(), -# }; -# -# let (_, body) = serde_json_error_response(err); -# assert_eq!(body, "Invalid JSON at line 1 column 1"); -# } -``` - -Note that while this approach works it might break in the future if axum changes -its implementation to use a different error type internally. Such changes might -happen without major breaking versions. - -# Defining custom extractors - -You can also define your own extractors by implementing either -[`FromRequestParts`] or [`FromRequest`]. - -## Implementing `FromRequestParts` - -Implement `FromRequestParts` if your extractor doesn't need access to the -request body: - -```rust,no_run -use axum::{ - extract::FromRequestParts, - routing::get, - Router, - http::{ - StatusCode, - header::{HeaderValue, USER_AGENT}, - request::Parts, - }, -}; - -struct ExtractUserAgent(HeaderValue); - -impl FromRequestParts for ExtractUserAgent -where - S: Send + Sync, -{ - type Rejection = (StatusCode, &'static str); - - async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { - if let Some(user_agent) = parts.headers.get(USER_AGENT) { - Ok(ExtractUserAgent(user_agent.clone())) - } else { - Err((StatusCode::BAD_REQUEST, "`User-Agent` header is missing")) - } - } -} - -async fn handler(ExtractUserAgent(user_agent): ExtractUserAgent) { - // ... -} - -let app = Router::new().route("/foo", get(handler)); -# let _: Router = app; -``` - -## Implementing `FromRequest` - -If your extractor needs to consume the request body you must implement [`FromRequest`] - -```rust,no_run -use axum::{ - extract::{Request, FromRequest}, - response::{Response, IntoResponse}, - body::{Bytes, Body}, - routing::get, - Router, - http::{ - StatusCode, - header::{HeaderValue, USER_AGENT}, - }, -}; - -struct ValidatedBody(Bytes); - -impl FromRequest for ValidatedBody -where - Bytes: FromRequest, - S: Send + Sync, -{ - type Rejection = Response; - - async fn from_request(req: Request, state: &S) -> Result { - let body = Bytes::from_request(req, state) - .await - .map_err(IntoResponse::into_response)?; - - // do validation... - - Ok(Self(body)) - } -} - -async fn handler(ValidatedBody(body): ValidatedBody) { - // ... -} - -let app = Router::new().route("/foo", get(handler)); -# let _: Router = app; -``` - -## Cannot implement both `FromRequest` and `FromRequestParts` - -Note that you will make your extractor unusable by implementing both -`FromRequest` and `FromRequestParts` directly for the same type, unless it is -wrapping another extractor: - -```rust,compile_fail -use axum::{ - Router, - routing::get, - extract::{FromRequest, Request, FromRequestParts}, - http::request::Parts, - body::Body, -}; -use std::convert::Infallible; - -// Some extractor that doesn't wrap another extractor -struct MyExtractor; - -// `MyExtractor` implements both `FromRequest` -impl FromRequest for MyExtractor -where - S: Send + Sync, -{ - type Rejection = Infallible; - - async fn from_request(req: Request, state: &S) -> Result { - // ... - # todo!() - } -} - -// and `FromRequestParts` -impl FromRequestParts for MyExtractor -where - S: Send + Sync, -{ - type Rejection = Infallible; - - async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { - // ... - # todo!() - } -} - -let app = Router::new().route( - "/", - // This fails when we go to actually use `MyExtractor` in a handler function. - // This is due to a limit in Rust's type system. - // - // The workaround is to implement either `FromRequest` or `FromRequestParts` - // but not both, if your extractor doesn't wrap another extractor. - // - // See "Wrapping extractors" for how to wrap other extractors. - get(|_: MyExtractor| async {}), -); -# let _: Router = app; -``` - -# Accessing other extractors in `FromRequest` or `FromRequestParts` implementations - -When defining custom extractors you often need to access another extractor -in your implementation. - -```rust -use axum::{ - extract::{Extension, FromRequestParts}, - http::{StatusCode, HeaderMap, request::Parts}, - response::{IntoResponse, Response}, - routing::get, - Router, -}; - -#[derive(Clone)] -struct State { - // ... -} - -struct AuthenticatedUser { - // ... -} - -impl FromRequestParts for AuthenticatedUser -where - S: Send + Sync, -{ - type Rejection = Response; - - async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { - // You can either call them directly... - let headers = HeaderMap::from_request_parts(parts, state) - .await - .map_err(|err| match err {})?; - - // ... or use `extract` / `extract_with_state` from `RequestExt` / `RequestPartsExt` - use axum::RequestPartsExt; - let Extension(state) = parts.extract::>() - .await - .map_err(|err| err.into_response())?; - - unimplemented!("actually perform the authorization") - } -} - -async fn handler(user: AuthenticatedUser) { - // ... -} - -let state = State { /* ... */ }; - -let app = Router::new().route("/", get(handler)).layer(Extension(state)); -# let _: Router = app; -``` - -# Request body limits - -For security reasons, [`Bytes`] will, by default, not accept bodies larger than -2MB. This also applies to extractors that uses [`Bytes`] internally such as -`String`, [`Json`], and [`Form`]. - -For more details, including how to disable this limit, see [`DefaultBodyLimit`]. - -# Wrapping extractors - -If you want write an extractor that generically wraps another extractor (that -may or may not consume the request body) you should implement both -[`FromRequest`] and [`FromRequestParts`]: - -```rust -use axum::{ - Router, - body::Body, - routing::get, - extract::{Request, FromRequest, FromRequestParts}, - http::{HeaderMap, request::Parts}, -}; -use std::time::{Instant, Duration}; - -// an extractor that wraps another and measures how long time it takes to run -struct Timing { - extractor: E, - duration: Duration, -} - -// we must implement both `FromRequestParts` -impl FromRequestParts for Timing -where - S: Send + Sync, - T: FromRequestParts, -{ - type Rejection = T::Rejection; - - async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { - let start = Instant::now(); - let extractor = T::from_request_parts(parts, state).await?; - let duration = start.elapsed(); - Ok(Timing { - extractor, - duration, - }) - } -} - -// and `FromRequest` -impl FromRequest for Timing -where - S: Send + Sync, - T: FromRequest, -{ - type Rejection = T::Rejection; - - async fn from_request(req: Request, state: &S) -> Result { - let start = Instant::now(); - let extractor = T::from_request(req, state).await?; - let duration = start.elapsed(); - Ok(Timing { - extractor, - duration, - }) - } -} - -async fn handler( - // this uses the `FromRequestParts` impl - _: Timing, - // this uses the `FromRequest` impl - _: Timing, -) {} -# let _: axum::routing::MethodRouter = axum::routing::get(handler); -``` - -# Logging rejections - -All built-in extractors will log rejections for easier debugging. To see the -logs, enable the `tracing` feature for axum (enabled by default) and the -`axum::rejection=trace` tracing target, for example with -`RUST_LOG=info,axum::rejection=trace cargo run`. - -[axum-extra]: https://docs.rs/axum-extra/latest/axum_extra/extract/index.html -[`body::Body`]: crate::body::Body -[`Bytes`]: crate::body::Bytes -[customize-extractor-error]: https://github.com/tokio-rs/axum/blob/main/examples/customize-extractor-error/src/main.rs -[`HeaderMap`]: https://docs.rs/http/latest/http/header/struct.HeaderMap.html -[`Request`]: https://docs.rs/http/latest/http/struct.Request.html -[`JsonRejection::JsonDataError`]: rejection::JsonRejection::JsonDataError diff --git a/axum/src/docs/handlers_intro.md b/axum/src/docs/handlers_intro.md deleted file mode 100644 index 4b42d420..00000000 --- a/axum/src/docs/handlers_intro.md +++ /dev/null @@ -1,8 +0,0 @@ -In axum a "handler" is an async function that accepts zero or more -["extractors"](crate::extract) as arguments and returns something that -can be converted [into a response](crate::response). - -Handlers are where your application logic lives and axum applications are built -by routing between handlers. - -[`debug_handler`]: https://docs.rs/axum-macros/latest/axum_macros/attr.debug_handler.html diff --git a/axum/src/docs/method_routing/fallback.md b/axum/src/docs/method_routing/fallback.md deleted file mode 100644 index e6f364a8..00000000 --- a/axum/src/docs/method_routing/fallback.md +++ /dev/null @@ -1,55 +0,0 @@ -Add a fallback service to the router. - -This service will be called if no routes matches the incoming request. - -```rust -use axum::{ - Router, - routing::get, - handler::Handler, - response::IntoResponse, - http::{StatusCode, Method, Uri}, -}; - -let handler = get(|| async {}).fallback(fallback); - -let app = Router::new().route("/", handler); - -async fn fallback(method: Method, uri: Uri) -> (StatusCode, String) { - (StatusCode::NOT_FOUND, format!("`{method}` not allowed for {uri}")) -} -# let _: Router = app; -``` - -## When used with `MethodRouter::merge` - -Two routers that both have a fallback cannot be merged. Doing so results in a -panic: - -```rust,should_panic -use axum::{ - routing::{get, post}, - handler::Handler, - response::IntoResponse, - http::{StatusCode, Uri}, -}; - -let one = get(|| async {}).fallback(fallback_one); - -let two = post(|| async {}).fallback(fallback_two); - -let method_route = one.merge(two); - -async fn fallback_one() -> impl IntoResponse { /* ... */ } -async fn fallback_two() -> impl IntoResponse { /* ... */ } -# let app: axum::Router = axum::Router::new().route("/", method_route); -``` - -## Setting the `Allow` header - -By default `MethodRouter` will set the `Allow` header when returning `405 Method -Not Allowed`. This is also done when the fallback is used unless the response -generated by the fallback already sets the `Allow` header. - -This means if you use `fallback` to accept additional methods, you should make -sure you set the `Allow` header correctly. diff --git a/axum/src/docs/method_routing/layer.md b/axum/src/docs/method_routing/layer.md deleted file mode 100644 index e155ee51..00000000 --- a/axum/src/docs/method_routing/layer.md +++ /dev/null @@ -1,27 +0,0 @@ -Apply a [`tower::Layer`] to all routes in the router. - -This can be used to add additional processing to a request for a group -of routes. - -Note that the middleware is only applied to existing routes. So you have to -first add your routes (and / or fallback) and then call `layer` afterwards. Additional -routes added after `layer` is called will not have the middleware added. - -Works similarly to [`Router::layer`](super::Router::layer). See that method for -more details. - -# Example - -```rust -use axum::{routing::get, Router}; -use tower::limit::ConcurrencyLimitLayer; - -async fn handler() {} - -let app = Router::new().route( - "/", - // All requests to `GET /` will be sent through `ConcurrencyLimitLayer` - get(handler).layer(ConcurrencyLimitLayer::new(64)), -); -# let _: Router = app; -``` diff --git a/axum/src/docs/method_routing/merge.md b/axum/src/docs/method_routing/merge.md deleted file mode 100644 index a88ee2d7..00000000 --- a/axum/src/docs/method_routing/merge.md +++ /dev/null @@ -1,23 +0,0 @@ -Merge two routers into one. - -This is useful for breaking routers into smaller pieces and combining them -into one. - -```rust -use axum::{ - routing::{get, post}, - Router, -}; - -let get = get(|| async {}); -let post = post(|| async {}); - -let merged = get.merge(post); - -let app = Router::new().route("/", merged); - -// Our app now accepts -// - GET / -// - POST / -# let _: Router = app; -``` diff --git a/axum/src/docs/method_routing/route_layer.md b/axum/src/docs/method_routing/route_layer.md deleted file mode 100644 index 501b5517..00000000 --- a/axum/src/docs/method_routing/route_layer.md +++ /dev/null @@ -1,33 +0,0 @@ -Apply a [`tower::Layer`] to the router that will only run if the request matches -a route. - -Note that the middleware is only applied to existing routes. So you have to -first add your routes (and / or fallback) and then call `route_layer` -afterwards. Additional routes added after `route_layer` is called will not have -the middleware added. - -This works similarly to [`MethodRouter::layer`] except the middleware will only run if -the request matches a route. This is useful for middleware that return early -(such as authorization) which might otherwise convert a `405 Method Not Allowed` into a -`401 Unauthorized`. - -# Example - -```rust -use axum::{ - routing::get, - Router, -}; -use tower_http::validate_request::ValidateRequestHeaderLayer; - -let app = Router::new().route( - "/foo", - get(|| async {}) - .route_layer(ValidateRequestHeaderLayer::bearer("password")) -); - -// `GET /foo` with a valid token will receive `200 OK` -// `GET /foo` with a invalid token will receive `401 Unauthorized` -// `POST /FOO` with a invalid token will receive `405 Method Not Allowed` -# let _: Router = app; -``` diff --git a/axum/src/docs/middleware.md b/axum/src/docs/middleware.md deleted file mode 100644 index a0e14c65..00000000 --- a/axum/src/docs/middleware.md +++ /dev/null @@ -1,583 +0,0 @@ -# Intro - -axum is unique in that it doesn't have its own bespoke middleware system and -instead integrates with [`tower`]. This means the ecosystem of [`tower`] and -[`tower-http`] middleware all work with axum. - -While it's not necessary to fully understand tower to write or use middleware -with axum, having at least a basic understanding of tower's concepts is -recommended. See [tower's guides][tower-guides] for a general introduction. -Reading the documentation for [`tower::ServiceBuilder`] is also recommended. - -# Applying middleware - -axum allows you to add middleware just about anywhere - -- To entire routers with [`Router::layer`] and [`Router::route_layer`]. -- To method routers with [`MethodRouter::layer`] and [`MethodRouter::route_layer`]. -- To individual handlers with [`Handler::layer`]. - -## Applying multiple middleware - -It's recommended to use [`tower::ServiceBuilder`] to apply multiple middleware at -once, instead of calling `layer` (or `route_layer`) repeatedly: - -```rust -use axum::{ - routing::get, - Extension, - Router, -}; -use tower_http::{trace::TraceLayer}; -use tower::ServiceBuilder; - -async fn handler() {} - -#[derive(Clone)] -struct State {} - -let app = Router::new() - .route("/", get(handler)) - .layer( - ServiceBuilder::new() - .layer(TraceLayer::new_for_http()) - .layer(Extension(State {})) - ); -# let _: Router = app; -``` - -# Commonly used middleware - -Some commonly used middleware are: - -- [`TraceLayer`](tower_http::trace) for high level tracing/logging. -- [`CorsLayer`](tower_http::cors) for handling CORS. -- [`CompressionLayer`](tower_http::compression) for automatic compression of responses. -- [`RequestIdLayer`](tower_http::request_id) and - [`PropagateRequestIdLayer`](tower_http::request_id) set and propagate request - ids. -- [`TimeoutLayer`](tower_http::timeout::TimeoutLayer) for timeouts. - -# Ordering - -When you add middleware with [`Router::layer`] (or similar) all previously added -routes will be wrapped in the middleware. Generally speaking, this results in -middleware being executed from bottom to top. - -So if you do this: - -```rust -use axum::{routing::get, Router}; - -async fn handler() {} - -# let layer_one = axum::Extension(()); -# let layer_two = axum::Extension(()); -# let layer_three = axum::Extension(()); -# -let app = Router::new() - .route("/", get(handler)) - .layer(layer_one) - .layer(layer_two) - .layer(layer_three); -# let _: Router = app; -``` - -Think of the middleware as being layered like an onion where each new layer -wraps all previous layers: - -```not_rust - requests - | - v -+----- layer_three -----+ -| +---- layer_two ----+ | -| | +-- layer_one --+ | | -| | | | | | -| | | handler | | | -| | | | | | -| | +-- layer_one --+ | | -| +---- layer_two ----+ | -+----- layer_three -----+ - | - v - responses -``` - -That is: - -- First `layer_three` receives the request -- It then does its thing and passes the request onto `layer_two` -- Which passes the request onto `layer_one` -- Which passes the request onto `handler` where a response is produced -- That response is then passed to `layer_one` -- Then to `layer_two` -- And finally to `layer_three` where it's returned out of your app - -It's a little more complicated in practice because any middleware is free to -return early and not call the next layer, for example if a request cannot be -authorized, but it's a useful mental model to have. - -As previously mentioned it's recommended to add multiple middleware using -`tower::ServiceBuilder`, however this impacts ordering: - -```rust -use tower::ServiceBuilder; -use axum::{routing::get, Router}; - -async fn handler() {} - -# let layer_one = axum::Extension(()); -# let layer_two = axum::Extension(()); -# let layer_three = axum::Extension(()); -# -let app = Router::new() - .route("/", get(handler)) - .layer( - ServiceBuilder::new() - .layer(layer_one) - .layer(layer_two) - .layer(layer_three), - ); -# let _: Router = app; -``` - -`ServiceBuilder` works by composing all layers into one such that they run top -to bottom. So with the previous code `layer_one` would receive the request -first, then `layer_two`, then `layer_three`, then `handler`, and then the -response would bubble back up through `layer_three`, then `layer_two`, and -finally `layer_one`. - -Executing middleware top to bottom is generally easier to understand and follow -mentally which is one of the reasons `ServiceBuilder` is recommended. - -# Writing middleware - -axum offers many ways of writing middleware, at different levels of abstraction -and with different pros and cons. - -## `axum::middleware::from_fn` - -Use [`axum::middleware::from_fn`] to write your middleware when: - -- You're not comfortable with implementing your own futures and would rather use - the familiar `async`/`await` syntax. -- You don't intend to publish your middleware as a crate for others to use. - Middleware written like this are only compatible with axum. - -## `axum::middleware::from_extractor` - -Use [`axum::middleware::from_extractor`] to write your middleware when: - -- You have a type that you sometimes want to use as an extractor and sometimes - as a middleware. If you only need your type as a middleware prefer - [`middleware::from_fn`]. - -## tower's combinators - -tower has several utility combinators that can be used to perform simple -modifications to requests or responses. The most commonly used ones are - -- [`ServiceBuilder::map_request`] -- [`ServiceBuilder::map_response`] -- [`ServiceBuilder::then`] -- [`ServiceBuilder::and_then`] - -You should use these when - -- You want to perform a small ad hoc operation, such as adding a header. -- You don't intend to publish your middleware as a crate for others to use. - -## `tower::Service` and `Pin>` - -For maximum control (and a more low level API) you can write your own middleware -by implementing [`tower::Service`]: - -Use [`tower::Service`] with `Pin>` to write your middleware when: - -- Your middleware needs to be configurable for example via builder methods on - your [`tower::Layer`] such as [`tower_http::trace::TraceLayer`]. -- You do intend to publish your middleware as a crate for others to use. -- You're not comfortable with implementing your own futures. - -A decent template for such a middleware could be: - -```rust -use axum::{ - response::Response, - body::Body, - extract::Request, -}; -use futures_core::future::BoxFuture; -use tower::{Service, Layer}; -use std::task::{Context, Poll}; - -#[derive(Clone)] -struct MyLayer; - -impl Layer for MyLayer { - type Service = MyMiddleware; - - fn layer(&self, inner: S) -> Self::Service { - MyMiddleware { inner } - } -} - -#[derive(Clone)] -struct MyMiddleware { - inner: S, -} - -impl Service for MyMiddleware -where - S: Service + Send + 'static, - S::Future: Send + 'static, -{ - type Response = S::Response; - type Error = S::Error; - // `BoxFuture` is a type alias for `Pin>` - type Future = BoxFuture<'static, Result>; - - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - self.inner.poll_ready(cx) - } - - fn call(&mut self, request: Request) -> Self::Future { - let future = self.inner.call(request); - Box::pin(async move { - let response: Response = future.await?; - Ok(response) - }) - } -} -``` - -Note that your error type being defined as `S::Error` means that your middleware typically _returns no errors_. As a principle always try to return a response and try not to bail out with a custom error type. For example, if a 3rd party library you are using inside your new middleware returns its own specialized error type, try to convert it to some reasonable response and return `Ok` with that response. - -If you choose to implement a custom error type such as `type Error = BoxError` (a boxed opaque error), or any other error type that is not `Infallible`, you must use a `HandleErrorLayer`, here is an example using a `ServiceBuilder`: - -```ignore -ServiceBuilder::new() - .layer(HandleErrorLayer::new(|_: BoxError| async { - // because axum uses infallible errors, you must handle your custom error type from your middleware here - StatusCode::BAD_REQUEST - })) - .layer( - // - ); -``` - -## `tower::Service` and custom futures - -If you're comfortable implementing your own futures (or want to learn it) and -need as much control as possible then using `tower::Service` without boxed -futures is the way to go. - -Use [`tower::Service`] with manual futures to write your middleware when: - -- You want your middleware to have the lowest possible overhead. -- Your middleware needs to be configurable for example via builder methods on - your [`tower::Layer`] such as [`tower_http::trace::TraceLayer`]. -- You do intend to publish your middleware as a crate for others to use, perhaps - as part of tower-http. -- You're comfortable with implementing your own futures, or want to learn how - the lower levels of async Rust works. - -tower's ["Building a middleware from scratch"][tower-from-scratch-guide] -guide is a good place to learn how to do this. - -# Error handling for middleware - -axum's error handling model requires handlers to always return a response. -However middleware is one possible way to introduce errors into an application. -If hyper receives an error the connection will be closed without sending a -response. Thus axum requires those errors to be handled gracefully: - -```rust -use axum::{ - routing::get, - error_handling::HandleErrorLayer, - http::StatusCode, - BoxError, - Router, -}; -use tower::{ServiceBuilder, timeout::TimeoutLayer}; -use std::time::Duration; - -async fn handler() {} - -let app = Router::new() - .route("/", get(handler)) - .layer( - ServiceBuilder::new() - // this middleware goes above `TimeoutLayer` because it will receive - // errors returned by `TimeoutLayer` - .layer(HandleErrorLayer::new(|_: BoxError| async { - StatusCode::REQUEST_TIMEOUT - })) - .layer(TimeoutLayer::new(Duration::from_secs(10))) - ); -# let _: Router = app; -``` - -See [`error_handling`](crate::error_handling) for more details on axum's error -handling model. - -# Routing to services/middleware and backpressure - -Generally routing to one of multiple services and backpressure doesn't mix -well. Ideally you would want ensure a service is ready to receive a request -before calling it. However, in order to know which service to call, you need -the request... - -One approach is to not consider the router service itself ready until all -destination services are ready. That is the approach used by -[`tower::steer::Steer`]. - -Another approach is to always consider all services ready (always return -`Poll::Ready(Ok(()))`) from `Service::poll_ready` and then actually drive -readiness inside the response future returned by `Service::call`. This works -well when your services don't care about backpressure and are always ready -anyway. - -axum expects that all services used in your app won't care about -backpressure and so it uses the latter strategy. However that means you -should avoid routing to a service (or using a middleware) that _does_ care -about backpressure. At the very least you should [load shed][tower::load_shed] -so requests are dropped quickly and don't keep piling up. - -It also means that if `poll_ready` returns an error then that error will be -returned in the response future from `call` and _not_ from `poll_ready`. In -that case, the underlying service will _not_ be discarded and will continue -to be used for future requests. Services that expect to be discarded if -`poll_ready` fails should _not_ be used with axum. - -One possible approach is to only apply backpressure sensitive middleware -around your entire app. This is possible because axum applications are -themselves services: - -```rust -use axum::{ - routing::get, - Router, -}; -use tower::ServiceBuilder; -# let some_backpressure_sensitive_middleware = -# tower::layer::util::Identity::new(); - -async fn handler() { /* ... */ } - -let app = Router::new().route("/", get(handler)); - -let app = ServiceBuilder::new() - .layer(some_backpressure_sensitive_middleware) - .service(app); -# let _: Router = app; -``` - -However when applying middleware around your whole application in this way -you have to take care that errors are still being handled appropriately. - -Also note that handlers created from async functions don't care about -backpressure and are always ready. So if you're not using any Tower -middleware you don't have to worry about any of this. - -# Accessing state in middleware - -How to make state available to middleware depends on how the middleware is -written. - -## Accessing state in `axum::middleware::from_fn` - -Use [`axum::middleware::from_fn_with_state`](crate::middleware::from_fn_with_state). - -## Accessing state in custom `tower::Layer`s - -```rust -use axum::{ - Router, - routing::get, - middleware::{self, Next}, - response::Response, - extract::{State, Request}, -}; -use tower::{Layer, Service}; -use std::task::{Context, Poll}; - -#[derive(Clone)] -struct AppState {} - -#[derive(Clone)] -struct MyLayer { - state: AppState, -} - -impl Layer for MyLayer { - type Service = MyService; - - fn layer(&self, inner: S) -> Self::Service { - MyService { - inner, - state: self.state.clone(), - } - } -} - -#[derive(Clone)] -struct MyService { - inner: S, - state: AppState, -} - -impl Service> for MyService -where - S: Service>, -{ - type Response = S::Response; - type Error = S::Error; - type Future = S::Future; - - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - self.inner.poll_ready(cx) - } - - fn call(&mut self, req: Request) -> Self::Future { - // Do something with `self.state`. - // - // See `axum::RequestExt` for how to run extractors directly from - // a `Request`. - - self.inner.call(req) - } -} - -async fn handler(_: State) {} - -let state = AppState {}; - -let app = Router::new() - .route("/", get(handler)) - .layer(MyLayer { state: state.clone() }) - .with_state(state); -# let _: axum::Router = app; -``` - -# Passing state from middleware to handlers - -State can be passed from middleware to handlers using [request extensions]: - -```rust -use axum::{ - Router, - http::StatusCode, - routing::get, - response::{IntoResponse, Response}, - middleware::{self, Next}, - extract::{Request, Extension}, -}; - -#[derive(Clone)] -struct CurrentUser { /* ... */ } - -async fn auth(mut req: Request, next: Next) -> Result { - let auth_header = req.headers() - .get(http::header::AUTHORIZATION) - .and_then(|header| header.to_str().ok()); - - let auth_header = if let Some(auth_header) = auth_header { - auth_header - } else { - return Err(StatusCode::UNAUTHORIZED); - }; - - if let Some(current_user) = authorize_current_user(auth_header).await { - // insert the current user into a request extension so the handler can - // extract it - req.extensions_mut().insert(current_user); - Ok(next.run(req).await) - } else { - Err(StatusCode::UNAUTHORIZED) - } -} - -async fn authorize_current_user(auth_token: &str) -> Option { - // ... - # unimplemented!() -} - -async fn handler( - // extract the current user, set by the middleware - Extension(current_user): Extension, -) { - // ... -} - -let app = Router::new() - .route("/", get(handler)) - .route_layer(middleware::from_fn(auth)); -# let _: Router = app; -``` - -[Response extensions] can also be used but note that request extensions are not -automatically moved to response extensions. You need to manually do that for the -extensions you need. - -# Rewriting request URI in middleware - -Middleware added with [`Router::layer`] will run after routing. That means it -cannot be used to run middleware that rewrites the request URI. By the time the -middleware runs the routing is already done. - -The workaround is to wrap the middleware around the entire `Router` (this works -because `Router` implements [`Service`]): - -```rust -use tower::Layer; -use axum::{ - Router, - ServiceExt, // for `into_make_service` - response::Response, - middleware::Next, - extract::Request, -}; - -fn rewrite_request_uri(req: Request) -> Request { - // ... - # req -} - -// this can be any `tower::Layer` -let middleware = tower::util::MapRequestLayer::new(rewrite_request_uri); - -let app = Router::new(); - -// apply the layer around the whole `Router` -// this way the middleware will run before `Router` receives the request -let app_with_middleware = middleware.layer(app); - -# async { -let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); -axum::serve(listener, app_with_middleware.into_make_service()).await.unwrap(); -# }; -``` - -[`tower`]: https://crates.io/crates/tower -[`tower-http`]: https://crates.io/crates/tower-http -[tower-guides]: https://github.com/tower-rs/tower/tree/master/guides -[`axum::middleware::from_fn`]: fn@crate::middleware::from_fn -[`middleware::from_fn`]: fn@crate::middleware::from_fn -[tower-from-scratch-guide]: https://github.com/tower-rs/tower/blob/master/guides/building-a-middleware-from-scratch.md -[`ServiceBuilder::map_request`]: tower::ServiceBuilder::map_request -[`ServiceBuilder::map_response`]: tower::ServiceBuilder::map_response -[`ServiceBuilder::then`]: tower::ServiceBuilder::then -[`ServiceBuilder::and_then`]: tower::ServiceBuilder::and_then -[`axum::middleware::from_extractor`]: fn@crate::middleware::from_extractor -[`Handler::layer`]: crate::handler::Handler::layer -[`Router::layer`]: crate::routing::Router::layer -[`MethodRouter::layer`]: crate::routing::MethodRouter::layer -[`Router::route_layer`]: crate::routing::Router::route_layer -[`MethodRouter::route_layer`]: crate::routing::MethodRouter::route_layer -[request extensions]: https://docs.rs/http/latest/http/request/struct.Request.html#method.extensions -[Response extensions]: https://docs.rs/http/latest/http/response/struct.Response.html#method.extensions -[`State`]: crate::extract::State -[`Service`]: tower::Service diff --git a/axum/src/docs/response.md b/axum/src/docs/response.md deleted file mode 100644 index c0974fb6..00000000 --- a/axum/src/docs/response.md +++ /dev/null @@ -1,321 +0,0 @@ -Types and traits for generating responses. - -# Building responses - -Anything that implements [`IntoResponse`] can be returned from a handler. axum -provides implementations for common types: - -```rust,no_run -use axum::{ - Json, - response::{Html, IntoResponse}, - http::{StatusCode, Uri, header::{self, HeaderMap, HeaderName}}, -}; - -// `()` gives an empty response -async fn empty() {} - -// String will get a `text/plain; charset=utf-8` content-type -async fn plain_text(uri: Uri) -> String { - format!("Hi from {}", uri.path()) -} - -// Bytes will get a `application/octet-stream` content-type -async fn bytes() -> Vec { - vec![1, 2, 3, 4] -} - -// `Json` will get a `application/json` content-type and work with anything that -// implements `serde::Serialize` -async fn json() -> Json> { - Json(vec!["foo".to_owned(), "bar".to_owned()]) -} - -// `Html` will get a `text/html` content-type -async fn html() -> Html<&'static str> { - Html("

Hello, World!

") -} - -// `StatusCode` gives an empty response with that status code -async fn status() -> StatusCode { - StatusCode::NOT_FOUND -} - -// `HeaderMap` gives an empty response with some headers -async fn headers() -> HeaderMap { - let mut headers = HeaderMap::new(); - headers.insert(header::SERVER, "axum".parse().unwrap()); - headers -} - -// An array of tuples also gives headers -async fn array_headers() -> [(HeaderName, &'static str); 2] { - [ - (header::SERVER, "axum"), - (header::CONTENT_TYPE, "text/plain") - ] -} - -// Use `impl IntoResponse` to avoid writing the whole type -async fn impl_trait() -> impl IntoResponse { - [ - (header::SERVER, "axum"), - (header::CONTENT_TYPE, "text/plain") - ] -} -``` - -Additionally you can return tuples to build more complex responses from -individual parts. - -```rust,no_run -use axum::{ - Json, - response::IntoResponse, - http::{StatusCode, HeaderMap, Uri, header}, - extract::Extension, -}; - -// `(StatusCode, impl IntoResponse)` will override the status code of the response -async fn with_status(uri: Uri) -> (StatusCode, String) { - (StatusCode::NOT_FOUND, format!("Not Found: {}", uri.path())) -} - -// Use `impl IntoResponse` to avoid having to type the whole type -async fn impl_trait(uri: Uri) -> impl IntoResponse { - (StatusCode::NOT_FOUND, format!("Not Found: {}", uri.path())) -} - -// `(HeaderMap, impl IntoResponse)` to add additional headers -async fn with_headers() -> impl IntoResponse { - let mut headers = HeaderMap::new(); - headers.insert(header::CONTENT_TYPE, "text/plain".parse().unwrap()); - (headers, "foo") -} - -// Or an array of tuples to more easily build the headers -async fn with_array_headers() -> impl IntoResponse { - ([(header::CONTENT_TYPE, "text/plain")], "foo") -} - -// Use string keys for custom headers -async fn with_array_headers_custom() -> impl IntoResponse { - ([("x-custom", "custom")], "foo") -} - -// `(StatusCode, headers, impl IntoResponse)` to set status and add headers -// `headers` can be either a `HeaderMap` or an array of tuples -async fn with_status_and_array_headers() -> impl IntoResponse { - ( - StatusCode::NOT_FOUND, - [(header::CONTENT_TYPE, "text/plain")], - "foo", - ) -} - -// `(Extension<_>, impl IntoResponse)` to set response extensions -async fn with_status_extensions() -> impl IntoResponse { - ( - Extension(Foo("foo")), - "foo", - ) -} - -#[derive(Clone)] -struct Foo(&'static str); - -// Or mix and match all the things -async fn all_the_things(uri: Uri) -> impl IntoResponse { - let mut header_map = HeaderMap::new(); - if uri.path() == "/" { - header_map.insert(header::SERVER, "axum".parse().unwrap()); - } - - ( - // set status code - StatusCode::NOT_FOUND, - // headers with an array - [("x-custom", "custom")], - // some extensions - Extension(Foo("foo")), - Extension(Foo("bar")), - // more headers, built dynamically - header_map, - // and finally the body - "foo", - ) -} -``` - -In general you can return tuples like: - -- `(StatusCode, impl IntoResponse)` -- `(Parts, impl IntoResponse)` -- `(Response<()>, impl IntoResponse)` -- `(T1, .., Tn, impl IntoResponse)` where `T1` to `Tn` all implement [`IntoResponseParts`]. -- `(StatusCode, T1, .., Tn, impl IntoResponse)` where `T1` to `Tn` all implement [`IntoResponseParts`]. -- `(Parts, T1, .., Tn, impl IntoResponse)` where `T1` to `Tn` all implement [`IntoResponseParts`]. -- `(Response<()>, T1, .., Tn, impl IntoResponse)` where `T1` to `Tn` all implement [`IntoResponseParts`]. - -This means you cannot accidentally override the status or body as [`IntoResponseParts`] only allows -setting headers and extensions. - -Use [`Response`] for more low level control: - -```rust,no_run -use axum::{ - Json, - response::{IntoResponse, Response}, - body::Body, - http::StatusCode, -}; - -async fn response() -> Response { - Response::builder() - .status(StatusCode::NOT_FOUND) - .header("x-foo", "custom header") - .body(Body::from("not found")) - .unwrap() -} -``` - -# Returning different response types - -If you need to return multiple response types, and `Result` isn't appropriate, you can call -`.into_response()` to turn things into `axum::response::Response`: - -```rust -use axum::{ - response::{IntoResponse, Redirect, Response}, - http::StatusCode, -}; - -async fn handle() -> Response { - if something() { - "All good!".into_response() - } else if something_else() { - ( - StatusCode::INTERNAL_SERVER_ERROR, - "Something went wrong...", - ).into_response() - } else { - Redirect::to("/").into_response() - } -} - -fn something() -> bool { - // ... - # true -} - -fn something_else() -> bool { - // ... - # true -} -``` - -# Regarding `impl IntoResponse` - -You can use `impl IntoResponse` as the return type from handlers to avoid -typing large types. For example - -```rust -use axum::http::StatusCode; - -async fn handler() -> (StatusCode, [(&'static str, &'static str); 1], &'static str) { - (StatusCode::OK, [("x-foo", "bar")], "Hello, World!") -} -``` - -Becomes easier using `impl IntoResponse`: - -```rust -use axum::{http::StatusCode, response::IntoResponse}; - -async fn impl_into_response() -> impl IntoResponse { - (StatusCode::OK, [("x-foo", "bar")], "Hello, World!") -} -``` - -However `impl IntoResponse` has a few limitations. Firstly it can only be used -to return a single type: - -```rust,compile_fail -use axum::{http::StatusCode, response::IntoResponse}; - -async fn handler() -> impl IntoResponse { - if check_something() { - StatusCode::NOT_FOUND - } else { - "Hello, World!" - } -} - -fn check_something() -> bool { - # false - // ... -} -``` - -This function returns either a `StatusCode` or a `&'static str` which `impl -Trait` doesn't allow. - -Secondly `impl IntoResponse` can lead to type inference issues when used with -`Result` and `?`: - -```rust,compile_fail -use axum::{http::StatusCode, response::IntoResponse}; - -async fn handler() -> impl IntoResponse { - create_thing()?; - Ok(StatusCode::CREATED) -} - -fn create_thing() -> Result<(), StatusCode> { - # Ok(()) - // ... -} -``` - -This is because `?` supports using the [`From`] trait to convert to a different -error type but it doesn't know which type to convert to, because we only -specified `impl IntoResponse` as the return type. - -`Result` doesn't always work either: - -```rust,compile_fail -use axum::{http::StatusCode, response::IntoResponse}; - -async fn handler() -> Result { - create_thing()?; - Ok(StatusCode::CREATED) -} - -fn create_thing() -> Result<(), StatusCode> { - # Ok(()) - // ... -} -``` - -The solution is to use a concrete error type, such as `Result`: - -```rust -use axum::{http::StatusCode, response::IntoResponse}; - -async fn handler() -> Result { - create_thing()?; - Ok(StatusCode::CREATED) -} - -fn create_thing() -> Result<(), StatusCode> { - # Ok(()) - // ... -} -``` - -Because of this it is generally not recommended to use `impl IntoResponse` -unless you're familiar with the details of how `impl Trait` works. - -[`IntoResponse`]: crate::response::IntoResponse -[`IntoResponseParts`]: crate::response::IntoResponseParts -[`StatusCode`]: http::StatusCode diff --git a/axum/src/docs/routing/fallback.md b/axum/src/docs/routing/fallback.md deleted file mode 100644 index a864b7a4..00000000 --- a/axum/src/docs/routing/fallback.md +++ /dev/null @@ -1,61 +0,0 @@ -Add a fallback [`Handler`] to the router. - -This service will be called if no routes matches the incoming request. - -```rust -use axum::{ - Router, - routing::get, - handler::Handler, - response::IntoResponse, - http::{StatusCode, Uri}, -}; - -let app = Router::new() - .route("/foo", get(|| async { /* ... */ })) - .fallback(fallback); - -async fn fallback(uri: Uri) -> (StatusCode, String) { - (StatusCode::NOT_FOUND, format!("No route for {uri}")) -} -# let _: Router = app; -``` - -Fallbacks only apply to routes that aren't matched by anything in the -router. If a handler is matched by a request but returns 404 the -fallback is not called. Note that this applies to [`MethodRouter`]s too: if the -request hits a valid path but the [`MethodRouter`] does not have an appropriate -method handler installed, the fallback is not called (use -[`MethodRouter::fallback`] for this purpose instead). - - -# Handling all requests without other routes - -Using `Router::new().fallback(...)` to accept all request regardless of path or -method, if you don't have other routes, isn't optimal: - -```rust -use axum::Router; - -async fn handler() {} - -let app = Router::new().fallback(handler); - -# async { -let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); -axum::serve(listener, app).await.unwrap(); -# }; -``` - -Running the handler directly is faster since it avoids the overhead of routing: - -```rust -use axum::handler::HandlerWithoutStateExt; - -async fn handler() {} - -# async { -let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); -axum::serve(listener, handler.into_make_service()).await.unwrap(); -# }; -``` diff --git a/axum/src/docs/routing/into_make_service_with_connect_info.md b/axum/src/docs/routing/into_make_service_with_connect_info.md deleted file mode 100644 index 088f21f9..00000000 --- a/axum/src/docs/routing/into_make_service_with_connect_info.md +++ /dev/null @@ -1,73 +0,0 @@ -Convert this router into a [`MakeService`], that will store `C`'s -associated `ConnectInfo` in a request extension such that [`ConnectInfo`] -can extract it. - -This enables extracting things like the client's remote address. - -Extracting [`std::net::SocketAddr`] is supported out of the box: - -```rust -use axum::{ - extract::ConnectInfo, - routing::get, - Router, -}; -use std::net::SocketAddr; - -let app = Router::new().route("/", get(handler)); - -async fn handler(ConnectInfo(addr): ConnectInfo) -> String { - format!("Hello {addr}") -} - -# async { -let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); -axum::serve(listener, app.into_make_service_with_connect_info::()).await.unwrap(); -# }; -``` - -You can implement custom a [`Connected`] like so: - -```rust -use axum::{ - extract::connect_info::{ConnectInfo, Connected}, - routing::get, - serve::IncomingStream, - Router, -}; -use tokio::net::TcpListener; - -let app = Router::new().route("/", get(handler)); - -async fn handler( - ConnectInfo(my_connect_info): ConnectInfo, -) -> String { - format!("Hello {my_connect_info:?}") -} - -#[derive(Clone, Debug)] -struct MyConnectInfo { - // ... -} - -impl Connected> for MyConnectInfo { - fn connect_info(target: IncomingStream<'_, TcpListener>) -> Self { - MyConnectInfo { - // ... - } - } -} - -# async { -let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); -axum::serve(listener, app.into_make_service_with_connect_info::()).await.unwrap(); -# }; -``` - -See the [unix domain socket example][uds] for an example of how to use -this to collect UDS connection info. - -[`MakeService`]: tower::make::MakeService -[`Connected`]: crate::extract::connect_info::Connected -[`ConnectInfo`]: crate::extract::connect_info::ConnectInfo -[uds]: https://github.com/tokio-rs/axum/blob/main/examples/unix-domain-socket/src/main.rs diff --git a/axum/src/docs/routing/layer.md b/axum/src/docs/routing/layer.md deleted file mode 100644 index 1c029c7f..00000000 --- a/axum/src/docs/routing/layer.md +++ /dev/null @@ -1,67 +0,0 @@ -Apply a [`tower::Layer`] to all routes in the router. - -This can be used to add additional processing to a request for a group -of routes. - -Note that the middleware is only applied to existing routes. So you have to -first add your routes (and / or fallback) and then call `layer` afterwards. Additional -routes added after `layer` is called will not have the middleware added. - -If you want to add middleware to a single handler you can either use -[`MethodRouter::layer`] or [`Handler::layer`]. - -# Example - -Adding the [`tower_http::trace::TraceLayer`]: - -```rust -use axum::{routing::get, Router}; -use tower_http::trace::TraceLayer; - -let app = Router::new() - .route("/foo", get(|| async {})) - .route("/bar", get(|| async {})) - .layer(TraceLayer::new_for_http()); -# let _: Router = app; -``` - -If you need to write your own middleware see ["Writing -middleware"](crate::middleware#writing-middleware) for the different options. - -If you only want middleware on some routes you can use [`Router::merge`]: - -```rust -use axum::{routing::get, Router}; -use tower_http::{trace::TraceLayer, compression::CompressionLayer}; - -let with_tracing = Router::new() - .route("/foo", get(|| async {})) - .layer(TraceLayer::new_for_http()); - -let with_compression = Router::new() - .route("/bar", get(|| async {})) - .layer(CompressionLayer::new()); - -// Merge everything into one `Router` -let app = Router::new() - .merge(with_tracing) - .merge(with_compression); -# let _: Router = app; -``` - -# Multiple middleware - -It's recommended to use [`tower::ServiceBuilder`] when applying multiple -middleware. See [`middleware`](crate::middleware) for more details. - -# Runs after routing - -Middleware added with this method will run _after_ routing and thus cannot be -used to rewrite the request URI. See ["Rewriting request URI in -middleware"](crate::middleware#rewriting-request-uri-in-middleware) for more -details and a workaround. - -# Error handling - -See [`middleware`](crate::middleware) for details on how error handling impacts -middleware. diff --git a/axum/src/docs/routing/merge.md b/axum/src/docs/routing/merge.md deleted file mode 100644 index ddea6608..00000000 --- a/axum/src/docs/routing/merge.md +++ /dev/null @@ -1,80 +0,0 @@ -Merge the paths and fallbacks of two routers into a single [`Router`]. - -This is useful for breaking apps into smaller pieces and combining them -into one. - -```rust -use axum::{ - routing::get, - Router, -}; -# -# async fn users_list() {} -# async fn users_show() {} -# async fn teams_list() {} - -// define some routes separately -let user_routes = Router::new() - .route("/users", get(users_list)) - .route("/users/{id}", get(users_show)); - -let team_routes = Router::new() - .route("/teams", get(teams_list)); - -// combine them into one -let app = Router::new() - .merge(user_routes) - .merge(team_routes); - -// could also do `user_routes.merge(team_routes)` - -// Our app now accepts -// - GET /users -// - GET /users/{id} -// - GET /teams -# let _: Router = app; -``` - -# Merging routers with state - -When combining [`Router`]s with this method, each [`Router`] must have the -same type of state. If your routers have different types you can use -[`Router::with_state`] to provide the state and make the types match: - -```rust -use axum::{ - Router, - routing::get, - extract::State, -}; - -#[derive(Clone)] -struct InnerState {} - -#[derive(Clone)] -struct OuterState {} - -async fn inner_handler(state: State) {} - -let inner_router = Router::new() - .route("/bar", get(inner_handler)) - .with_state(InnerState {}); - -async fn outer_handler(state: State) {} - -let app = Router::new() - .route("/", get(outer_handler)) - .merge(inner_router) - .with_state(OuterState {}); -# let _: axum::Router = app; -``` - -# Merging routers with fallbacks - -When combining [`Router`]s with this method, the [fallback](Router::fallback) is also merged. -However only one of the routers can have a fallback. - -# Panics - -- If two routers that each have a [fallback](Router::fallback) are merged. This - is because `Router` only allows a single fallback. diff --git a/axum/src/docs/routing/method_not_allowed_fallback.md b/axum/src/docs/routing/method_not_allowed_fallback.md deleted file mode 100644 index 22905cd9..00000000 --- a/axum/src/docs/routing/method_not_allowed_fallback.md +++ /dev/null @@ -1,38 +0,0 @@ -Add a fallback [`Handler`] for the case where a route exists, but the method of the request is not supported. - -Sets a fallback on all previously registered [`MethodRouter`]s, -to be called when no matching method handler is set. - -```rust,no_run -use axum::{response::IntoResponse, routing::get, Router}; - -async fn hello_world() -> impl IntoResponse { - "Hello, world!\n" -} - -async fn default_fallback() -> impl IntoResponse { - "Default fallback\n" -} - -async fn handle_405() -> impl IntoResponse { - "Method not allowed fallback" -} - -#[tokio::main] -async fn main() { - let router = Router::new() - .route("/", get(hello_world)) - .fallback(default_fallback) - .method_not_allowed_fallback(handle_405); - - let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); - - axum::serve(listener, router).await.unwrap(); -} -``` - -The fallback only applies if there is a `MethodRouter` registered for a given path, -but the method used in the request is not specified. In the example, a `GET` on -`http://localhost:3000` causes the `hello_world` handler to react, while issuing a -`POST` triggers `handle_405`. Calling an entirely different route, like `http://localhost:3000/hello` -causes `default_fallback` to run. diff --git a/axum/src/docs/routing/nest.md b/axum/src/docs/routing/nest.md deleted file mode 100644 index bb5b2ea6..00000000 --- a/axum/src/docs/routing/nest.md +++ /dev/null @@ -1,194 +0,0 @@ -Nest a [`Router`] at some path. - -This allows you to break your application into smaller pieces and compose -them together. - -# Example - -```rust -use axum::{ - routing::{get, post}, - Router, -}; - -let user_routes = Router::new().route("/{id}", get(|| async {})); - -let team_routes = Router::new().route("/", post(|| async {})); - -let api_routes = Router::new() - .nest("/users", user_routes) - .nest("/teams", team_routes); - -let app = Router::new().nest("/api", api_routes); - -// Our app now accepts -// - GET /api/users/{id} -// - POST /api/teams -# let _: Router = app; -``` - -# How the URI changes - -Note that nested routes will not see the original request URI but instead -have the matched prefix stripped. This is necessary for services like static -file serving to work. Use [`OriginalUri`] if you need the original request -URI. - -# Captures from outer routes - -Take care when using `nest` together with dynamic routes as nesting also -captures from the outer routes: - -```rust -use axum::{ - extract::Path, - routing::get, - Router, -}; -use std::collections::HashMap; - -async fn users_get(Path(params): Path>) { - // 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); -# let _: Router = app; -``` - -# Differences from wildcard routes - -Nested routes are similar to wildcard routes. The difference is that -wildcard routes still see the whole URI whereas nested routes will have -the prefix stripped: - -```rust -use axum::{routing::get, http::Uri, Router}; - -let nested_router = Router::new() - .route("/", get(|uri: Uri| async { - // `uri` will _not_ contain `/bar` - })); - -let app = Router::new() - .route("/foo/{*rest}", get(|uri: Uri| async { - // `uri` will contain `/foo` - })) - .nest("/bar", nested_router); -# let _: Router = app; -``` - -Additionally, while the wildcard route `/foo/*rest` will not match the -paths `/foo` or `/foo/`, a nested router at `/foo` will match the path `/foo` -(but not `/foo/`), and a nested router at `/foo/` will match the path `/foo/` -(but not `/foo`). - -# Fallbacks - -If a nested router doesn't have its own fallback then it will inherit the -fallback from the outer router: - -```rust -use axum::{routing::get, http::StatusCode, handler::Handler, Router}; - -async fn fallback() -> (StatusCode, &'static str) { - (StatusCode::NOT_FOUND, "Not Found") -} - -let api_routes = Router::new().route("/users", get(|| async {})); - -let app = Router::new() - .nest("/api", api_routes) - .fallback(fallback); -# let _: Router = app; -``` - -Here requests like `GET /api/not-found` will go into `api_routes` but because -it doesn't have a matching route and doesn't have its own fallback it will call -the fallback from the outer router, i.e. the `fallback` function. - -If the nested router has its own fallback then the outer fallback will not be -inherited: - -```rust -use axum::{ - routing::get, - http::StatusCode, - handler::Handler, - Json, - Router, -}; - -async fn fallback() -> (StatusCode, &'static str) { - (StatusCode::NOT_FOUND, "Not Found") -} - -async fn api_fallback() -> (StatusCode, Json) { - ( - StatusCode::NOT_FOUND, - Json(serde_json::json!({ "status": "Not Found" })), - ) -} - -let api_routes = Router::new() - .route("/users", get(|| async {})) - .fallback(api_fallback); - -let app = Router::new() - .nest("/api", api_routes) - .fallback(fallback); -# let _: Router = app; -``` - -Here requests like `GET /api/not-found` will go to `api_fallback`. - -# Nesting routers with state - -When combining [`Router`]s with this method, each [`Router`] must have the -same type of state. If your routers have different types you can use -[`Router::with_state`] to provide the state and make the types match: - -```rust -use axum::{ - Router, - routing::get, - extract::State, -}; - -#[derive(Clone)] -struct InnerState {} - -#[derive(Clone)] -struct OuterState {} - -async fn inner_handler(state: State) {} - -let inner_router = Router::new() - .route("/bar", get(inner_handler)) - .with_state(InnerState {}); - -async fn outer_handler(state: State) {} - -let app = Router::new() - .route("/", get(outer_handler)) - .nest("/foo", inner_router) - .with_state(OuterState {}); -# let _: axum::Router = app; -``` - -Note that the inner router will still inherit the fallback from the outer -router. - -# Panics - -- If the route overlaps with another route. See [`Router::route`] - for more details. -- If the route contains a wildcard (`*`). -- If `path` is empty. - -[`OriginalUri`]: crate::extract::OriginalUri -[fallbacks]: Router::fallback diff --git a/axum/src/docs/routing/route.md b/axum/src/docs/routing/route.md deleted file mode 100644 index 528f8e1a..00000000 --- a/axum/src/docs/routing/route.md +++ /dev/null @@ -1,156 +0,0 @@ -Add another route to the router. - -`path` is a string of path segments separated by `/`. Each segment -can be either static, a capture, or a wildcard. - -`method_router` is the [`MethodRouter`] that should receive the request if the -path matches `path`. Usually, `method_router` will be a handler wrapped in a method -router like [`get`]. See [`handler`](crate::handler) for more details on handlers. - -# Static paths - -Examples: - -- `/` -- `/foo` -- `/users/123` - -If the incoming request matches the path exactly the corresponding service will -be called. - -# Captures - -Paths can contain segments like `/{key}` which matches any single segment and -will store the value captured at `key`. The value captured can be zero-length -except for in the invalid path `//`. - -Examples: - -- `/{key}` -- `/users/{id}` -- `/users/{id}/tweets` - -Captures can be extracted using [`Path`](crate::extract::Path). See its -documentation for more details. - -It is not possible to create segments that only match some types like numbers or -regular expression. You must handle that manually in your handlers. - -[`MatchedPath`] can be used to extract the matched path rather than the actual path. - -# Wildcards - -Paths can end in `/{*key}` which matches all segments and will store the segments -captured at `key`. - -Examples: - -- `/{*key}` -- `/assets/{*path}` -- `/{id}/{repo}/{*tree}` - -Note that `/{*key}` doesn't match empty segments. Thus: - -- `/{*key}` doesn't match `/` but does match `/a`, `/a/`, etc. -- `/x/{*key}` doesn't match `/x` or `/x/` but does match `/x/a`, `/x/a/`, etc. - -Wildcard captures can also be extracted using [`Path`](crate::extract::Path): - -```rust -use axum::{ - Router, - routing::get, - extract::Path, -}; - -let app: Router = Router::new().route("/{*key}", get(handler)); - -async fn handler(Path(path): Path) -> String { - path -} -``` - -Note that the leading slash is not included, i.e. for the route `/foo/{*rest}` and -the path `/foo/bar/baz` the value of `rest` will be `bar/baz`. - -# Accepting multiple methods - -To accept multiple methods for the same route you can add all handlers at the -same time: - -```rust -use axum::{Router, routing::{get, delete}, extract::Path}; - -let app = Router::new().route( - "/", - get(get_root).post(post_root).delete(delete_root), -); - -async fn get_root() {} - -async fn post_root() {} - -async fn delete_root() {} -# let _: Router = app; -``` - -Or you can add them one by one: - -```rust -# use axum::Router; -# use axum::routing::{get, post, delete}; -# -let app = Router::new() - .route("/", get(get_root)) - .route("/", post(post_root)) - .route("/", delete(delete_root)); -# -# let _: Router = app; -# async fn get_root() {} -# async fn post_root() {} -# async fn delete_root() {} -``` - -# More examples - -```rust -use axum::{Router, routing::{get, delete}, extract::Path}; - -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_users_action)) - .route("/assets/{*path}", get(serve_asset)); - -async fn root() {} - -async fn list_users() {} - -async fn create_user() {} - -async fn show_user(Path(id): Path) {} - -async fn do_users_action(Path((version, id)): Path<(String, u64)>) {} - -async fn serve_asset(Path(path): Path) {} -# let _: Router = app; -``` - -# Panics - -Panics if the route overlaps with another route: - -```rust,should_panic -use axum::{routing::get, Router}; - -let app = Router::new() - .route("/", get(|| async {})) - .route("/", get(|| async {})); -# let _: Router = app; -``` - -The static route `/foo` and the dynamic route `/{key}` are not considered to -overlap and `/foo` will take precedence. - -Also panics if `path` is empty. diff --git a/axum/src/docs/routing/route_layer.md b/axum/src/docs/routing/route_layer.md deleted file mode 100644 index 9cce3ea7..00000000 --- a/axum/src/docs/routing/route_layer.md +++ /dev/null @@ -1,35 +0,0 @@ -Apply a [`tower::Layer`] to the router that will only run if the request matches -a route. - -Note that the middleware is only applied to existing routes. So you have to -first add your routes (and / or fallback) and then call `route_layer` -afterwards. Additional routes added after `route_layer` is called will not have -the middleware added. - -This works similarly to [`Router::layer`] except the middleware will only run if -the request matches a route. This is useful for middleware that return early -(such as authorization) which might otherwise convert a `404 Not Found` into a -`401 Unauthorized`. - -This function will panic if no routes have been declared yet on the router, -since the new layer will have no effect, and this is typically a bug. -In generic code, you can test if that is the case first, by calling [`Router::has_routes`]. - -# Example - -```rust -use axum::{ - routing::get, - Router, -}; -use tower_http::validate_request::ValidateRequestHeaderLayer; - -let app = Router::new() - .route("/foo", get(|| async {})) - .route_layer(ValidateRequestHeaderLayer::bearer("password")); - -// `GET /foo` with a valid token will receive `200 OK` -// `GET /foo` with a invalid token will receive `401 Unauthorized` -// `GET /not-found` with a invalid token will receive `404 Not Found` -# let _: Router = app; -``` diff --git a/axum/src/docs/routing/route_service.md b/axum/src/docs/routing/route_service.md deleted file mode 100644 index 7f016105..00000000 --- a/axum/src/docs/routing/route_service.md +++ /dev/null @@ -1,70 +0,0 @@ -Add another route to the router that calls a [`Service`]. - -# Example - -```rust,no_run -use axum::{ - Router, - body::Body, - routing::{any_service, get_service}, - extract::Request, - http::StatusCode, - error_handling::HandleErrorLayer, -}; -use tower_http::services::ServeFile; -use http::Response; -use std::{convert::Infallible, io}; -use tower::service_fn; - -let app = Router::new() - .route( - // Any request to `/` goes to a service - "/", - // Services whose response body is not `axum::body::BoxBody` - // can be wrapped in `axum::routing::any_service` (or one of the other routing filters) - // to have the response body mapped - any_service(service_fn(|_: Request| async { - let res = Response::new(Body::from("Hi from `GET /`")); - Ok::<_, Infallible>(res) - })) - ) - .route_service( - "/foo", - // This service's response body is `axum::body::BoxBody` so - // it can be routed to directly. - service_fn(|req: Request| async move { - let body = Body::from(format!("Hi from `{} /foo`", req.method())); - let res = Response::new(body); - Ok::<_, Infallible>(res) - }) - ) - .route_service( - // GET `/static/Cargo.toml` goes to a service from tower-http - "/static/Cargo.toml", - ServeFile::new("Cargo.toml"), - ); -# let _: Router = app; -``` - -Routing to arbitrary services in this way has complications for backpressure -([`Service::poll_ready`]). See the [Routing to services and backpressure] module -for more details. - -# Panics - -Panics for the same reasons as [`Router::route`] or if you attempt to route to a -`Router`: - -```rust,should_panic -use axum::{routing::get, Router}; - -let app = Router::new().route_service( - "/", - Router::new().route("/foo", get(|| async {})), -); -# let _: Router = app; -``` - -Use [`Router::nest`] instead. - -[Routing to services and backpressure]: middleware/index.html#routing-to-servicesmiddleware-and-backpressure diff --git a/axum/src/docs/routing/with_state.md b/axum/src/docs/routing/with_state.md deleted file mode 100644 index 197741cf..00000000 --- a/axum/src/docs/routing/with_state.md +++ /dev/null @@ -1,231 +0,0 @@ -Provide the state for the router. State passed to this method is global and will be used -for all requests this router receives. That means it is not suitable for holding state derived from a request, such as authorization data extracted in a middleware. Use [`Extension`] instead for such data. - -```rust -use axum::{Router, routing::get, extract::State}; - -#[derive(Clone)] -struct AppState {} - -let routes = Router::new() - .route("/", get(|State(state): State| async { - // use state - })) - .with_state(AppState {}); - -# async { -let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); -axum::serve(listener, routes).await.unwrap(); -# }; -``` - -# Returning routers with states from functions - -When returning `Router`s from functions, it is generally recommended not to set the -state directly: - -```rust -use axum::{Router, routing::get, extract::State}; - -#[derive(Clone)] -struct AppState {} - -// Don't call `Router::with_state` here -fn routes() -> Router { - Router::new() - .route("/", get(|_: State| async {})) -} - -// Instead do it before you run the server -let routes = routes().with_state(AppState {}); - -# async { -let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); -axum::serve(listener, routes).await.unwrap(); -# }; -``` - -If you do need to provide the state, and you're _not_ nesting/merging the router -into another router, then return `Router` without any type parameters: - -```rust -# use axum::{Router, routing::get, extract::State}; -# #[derive(Clone)] -# struct AppState {} -# -// Don't return `Router` -fn routes(state: AppState) -> Router { - Router::new() - .route("/", get(|_: State| async {})) - .with_state(state) -} - -let routes = routes(AppState {}); - -# async { -let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); -axum::serve(listener, routes).await.unwrap(); -# }; -``` - -This is because we can only call `Router::into_make_service` on `Router<()>`, -not `Router`. See below for more details about why that is. - -Note that the state defaults to `()` so `Router` and `Router<()>` is the same. - -If you are nesting/merging the router it is recommended to use a generic state -type on the resulting router: - -```rust -# use axum::{Router, routing::get, extract::State}; -# #[derive(Clone)] -# struct AppState {} -# -fn routes(state: AppState) -> Router { - Router::new() - .route("/", get(|_: State| async {})) - .with_state(state) -} - -let routes = Router::new().nest("/api", routes(AppState {})); - -# async { -let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); -axum::serve(listener, routes).await.unwrap(); -# }; -``` - -# What `S` in `Router` means - -`Router` means a router that is _missing_ a state of type `S` to be able to -handle requests. It does _not_ mean a `Router` that _has_ a state of type `S`. - -For example: - -```rust -# use axum::{Router, routing::get, extract::State}; -# #[derive(Clone)] -# struct AppState {} -# -// A router that _needs_ an `AppState` to handle requests -let router: Router = Router::new() - .route("/", get(|_: State| async {})); - -// Once we call `Router::with_state` the router isn't missing -// the state anymore, because we just provided it -// -// Therefore the router type becomes `Router<()>`, i.e a router -// that is not missing any state -let router: Router<()> = router.with_state(AppState {}); - -// Only `Router<()>` has the `into_make_service` method. -// -// You cannot call `into_make_service` on a `Router` -// because it is still missing an `AppState`. -# async { -let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); -axum::serve(listener, router).await.unwrap(); -# }; -``` - -Perhaps a little counter intuitively, `Router::with_state` doesn't always return a -`Router<()>`. Instead you get to pick what the new missing state type is: - -```rust -# use axum::{Router, routing::get, extract::State}; -# #[derive(Clone)] -# struct AppState {} -# -let router: Router = Router::new() - .route("/", get(|_: State| async {})); - -// When we call `with_state` we're able to pick what the next missing state type is. -// Here we pick `String`. -let string_router: Router = router.with_state(AppState {}); - -// That allows us to add new routes that uses `String` as the state type -let string_router = string_router - .route("/needs-string", get(|_: State| async {})); - -// Provide the `String` and choose `()` as the new missing state. -let final_router: Router<()> = string_router.with_state("foo".to_owned()); - -// Since we have a `Router<()>` we can run it. -# async { -let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); -axum::serve(listener, final_router).await.unwrap(); -# }; -``` - -This why this returning `Router` after calling `with_state` doesn't -work: - -```rust,compile_fail -# use axum::{Router, routing::get, extract::State}; -# #[derive(Clone)] -# struct AppState {} -# -// This won't work because we're returning a `Router` -// i.e. we're saying we're still missing an `AppState` -fn routes(state: AppState) -> Router { - Router::new() - .route("/", get(|_: State| async {})) - .with_state(state) -} - -let app = routes(AppState {}); - -// We can only call `Router::into_make_service` on a `Router<()>` -// but `app` is a `Router` -# async { -let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); -axum::serve(listener, app).await.unwrap(); -# }; -``` - -Instead return `Router<()>` since we have provided all the state needed: - -```rust -# use axum::{Router, routing::get, extract::State}; -# #[derive(Clone)] -# struct AppState {} -# -// We've provided all the state necessary so return `Router<()>` -fn routes(state: AppState) -> Router<()> { - Router::new() - .route("/", get(|_: State| async {})) - .with_state(state) -} - -let app = routes(AppState {}); - -// We can now call `Router::into_make_service` -# async { -let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); -axum::serve(listener, app).await.unwrap(); -# }; -``` - -# A note about performance - -If you need a `Router` that implements `Service` but you don't need any state (perhaps -you're making a library that uses axum internally) then it is recommended to call this -method before you start serving requests: - -```rust -use axum::{Router, routing::get}; - -let app = Router::new() - .route("/", get(|| async { /* ... */ })) - // even though we don't need any state, call `with_state(())` anyway - .with_state(()); -# let _: Router = app; -``` - -This is not required but it gives axum a chance to update some internals in the router -which may impact performance and reduce allocations. - -Note that [`Router::into_make_service`] and [`Router::into_make_service_with_connect_info`] -do this automatically. - -[`Extension`]: crate::Extension diff --git a/axum/src/docs/routing/without_v07_checks.md b/axum/src/docs/routing/without_v07_checks.md deleted file mode 100644 index f1b465ea..00000000 --- a/axum/src/docs/routing/without_v07_checks.md +++ /dev/null @@ -1,43 +0,0 @@ -Turn off checks for compatibility with route matching syntax from 0.7. - -This allows usage of paths starting with a colon `:` or an asterisk `*` which are otherwise prohibited. - -# Example - -```rust -use axum::{ - routing::get, - Router, -}; - -let app = Router::<()>::new() - .without_v07_checks() - .route("/:colon", get(|| async {})) - .route("/*asterisk", get(|| async {})); - -// Our app now accepts -// - GET /:colon -// - GET /*asterisk -# let _: Router = app; -``` - -Adding such routes without calling this method first will panic. - -```rust,should_panic -use axum::{ - routing::get, - Router, -}; - -// This panics... -let app = Router::<()>::new() - .route("/:colon", get(|| async {})); -``` - -# Merging - -When two routers are merged, v0.7 checks are disabled for route registrations on the resulting router if both of the two routers had them also disabled. - -# Nesting - -Each router needs to have the checks explicitly disabled. Nesting a router with the checks either enabled or disabled has no effect on the outer router. diff --git a/axum/src/error_handling/mod.rs b/axum/src/error_handling/mod.rs index 90da3e97..6551fcae 100644 --- a/axum/src/error_handling/mod.rs +++ b/axum/src/error_handling/mod.rs @@ -1,4 +1,182 @@ -#![doc = include_str!("../docs/error_handling.md")] +//! Error handling model and utilities +//! +//! # axum's error handling model +//! +//! axum is based on [`tower::Service`] which bundles errors through its associated +//! `Error` type. If you have a [`Service`] that produces an error and that error +//! makes it all the way up to hyper, the connection will be terminated _without_ +//! sending a response. This is generally not desirable so axum makes sure you +//! always produce a response by relying on the type system. +//! +//! axum does this by requiring all services have [`Infallible`] as their error +//! type. `Infallible` is the error type for errors that can never happen. +//! +//! This means if you define a handler like: +//! +//! ```rust +//! use axum::http::StatusCode; +//! +//! async fn handler() -> Result { +//! # todo!() +//! // ... +//! } +//! ``` +//! +//! While it looks like it might fail with a `StatusCode` this actually isn't an +//! "error". If this handler returns `Err(some_status_code)` that will still be +//! converted into a [`Response`] and sent back to the client. This is done +//! through `StatusCode`'s [`IntoResponse`] implementation. +//! +//! It doesn't matter whether you return `Err(StatusCode::NOT_FOUND)` or +//! `Err(StatusCode::INTERNAL_SERVER_ERROR)`. These are not considered errors in +//! axum. +//! +//! Instead of a direct `StatusCode`, it makes sense to use intermediate error type +//! that can ultimately be converted to `Response`. This allows using `?` operator +//! in handlers. See those examples: +//! +//! * [`anyhow-error-response`][anyhow] for generic boxed errors +//! * [`error-handling`][error-handling] for application-specific detailed errors +//! +//! [anyhow]: https://github.com/tokio-rs/axum/blob/main/examples/anyhow-error-response/src/main.rs +//! [error-handling]: https://github.com/tokio-rs/axum/blob/main/examples/error-handling/src/main.rs +//! +//! This also applies to extractors. If an extractor doesn't match the request the +//! request will be rejected and a response will be returned without calling your +//! handler. See [`extract`](crate::extract) to learn more about handling extractor +//! failures. +//! +//! # Routing to fallible services +//! +//! You generally don't have to think about errors if you're only using async +//! functions as handlers. However if you're embedding general `Service`s or +//! applying middleware, which might produce errors you have to tell axum how to +//! convert those errors into responses. +//! +//! ```rust +//! use axum::{ +//! Router, +//! body::Body, +//! http::{Request, Response, StatusCode}, +//! error_handling::HandleError, +//! }; +//! +//! async fn thing_that_might_fail() -> Result<(), anyhow::Error> { +//! # Ok(()) +//! // ... +//! } +//! +//! // this service might fail with `anyhow::Error` +//! let some_fallible_service = tower::service_fn(|_req| async { +//! thing_that_might_fail().await?; +//! Ok::<_, anyhow::Error>(Response::new(Body::empty())) +//! }); +//! +//! let app = Router::new().route_service( +//! "/", +//! // we cannot route to `some_fallible_service` directly since it might fail. +//! // we have to use `handle_error` which converts its errors into responses +//! // and changes its error type from `anyhow::Error` to `Infallible`. +//! HandleError::new(some_fallible_service, handle_anyhow_error), +//! ); +//! +//! // handle errors by converting them into something that implements +//! // `IntoResponse` +//! async fn handle_anyhow_error(err: anyhow::Error) -> (StatusCode, String) { +//! ( +//! StatusCode::INTERNAL_SERVER_ERROR, +//! format!("Something went wrong: {err}"), +//! ) +//! } +//! # let _: Router = app; +//! ``` +//! +//! # Applying fallible middleware +//! +//! Similarly axum requires you to handle errors from middleware. That is done with +//! [`HandleErrorLayer`]: +//! +//! ```rust +//! use axum::{ +//! Router, +//! BoxError, +//! routing::get, +//! http::StatusCode, +//! error_handling::HandleErrorLayer, +//! }; +//! use std::time::Duration; +//! use tower::ServiceBuilder; +//! +//! let app = Router::new() +//! .route("/", get(|| async {})) +//! .layer( +//! ServiceBuilder::new() +//! // `timeout` will produce an error if the handler takes +//! // too long so we must handle those +//! .layer(HandleErrorLayer::new(handle_timeout_error)) +//! .timeout(Duration::from_secs(30)) +//! ); +//! +//! async fn handle_timeout_error(err: BoxError) -> (StatusCode, String) { +//! if err.is::() { +//! ( +//! StatusCode::REQUEST_TIMEOUT, +//! "Request took too long".to_string(), +//! ) +//! } else { +//! ( +//! StatusCode::INTERNAL_SERVER_ERROR, +//! format!("Unhandled internal error: {err}"), +//! ) +//! } +//! } +//! # let _: Router = app; +//! ``` +//! +//! # Running extractors for error handling +//! +//! `HandleErrorLayer` also supports running extractors: +//! +//! ```rust +//! use axum::{ +//! Router, +//! BoxError, +//! routing::get, +//! http::{StatusCode, Method, Uri}, +//! error_handling::HandleErrorLayer, +//! }; +//! use std::time::Duration; +//! use tower::ServiceBuilder; +//! +//! let app = Router::new() +//! .route("/", get(|| async {})) +//! .layer( +//! ServiceBuilder::new() +//! // `timeout` will produce an error if the handler takes +//! // too long so we must handle those +//! .layer(HandleErrorLayer::new(handle_timeout_error)) +//! .timeout(Duration::from_secs(30)) +//! ); +//! +//! async fn handle_timeout_error( +//! // `Method` and `Uri` are extractors so they can be used here +//! method: Method, +//! uri: Uri, +//! // the last argument must be the error itself +//! err: BoxError, +//! ) -> (StatusCode, String) { +//! ( +//! StatusCode::INTERNAL_SERVER_ERROR, +//! format!("`{method} {uri}` failed with {err}"), +//! ) +//! } +//! # let _: Router = app; +//! ``` +//! +//! [`tower::Service`]: `tower::Service` +//! [`Infallible`]: std::convert::Infallible +//! [`Response`]: crate::response::Response +//! [`IntoResponse`]: crate::response::IntoResponse use crate::{ extract::FromRequestParts, diff --git a/axum/src/extract/mod.rs b/axum/src/extract/mod.rs index d2b19b68..241cc139 100644 --- a/axum/src/extract/mod.rs +++ b/axum/src/extract/mod.rs @@ -1,4 +1,692 @@ -#![doc = include_str!("../docs/extract.md")] +//! Types and traits for extracting data from requests. +//! +//! # Intro +//! +//! A handler function is an async function that takes any number of +//! "extractors" as arguments. An extractor is a type that implements +//! [`FromRequest`] or [`FromRequestParts`]. +//! +//! For example, [`Json`] is an extractor that consumes the request body and +//! deserializes it as JSON into some target type: +//! +//! ```rust,no_run +//! use axum::{ +//! extract::Json, +//! routing::post, +//! handler::Handler, +//! Router, +//! }; +//! use serde::Deserialize; +//! +//! #[derive(Deserialize)] +//! struct CreateUser { +//! email: String, +//! password: String, +//! } +//! +//! async fn create_user(Json(payload): Json) { +//! // ... +//! } +//! +//! let app = Router::new().route("/users", post(create_user)); +//! # let _: Router = app; +//! ``` +//! +//! # Common extractors +//! +//! Some commonly used extractors are: +//! +//! ```rust,no_run +//! use axum::{ +//! extract::{Request, Json, Path, Extension, Query}, +//! routing::post, +//! http::header::HeaderMap, +//! body::{Bytes, Body}, +//! Router, +//! }; +//! use serde_json::Value; +//! use std::collections::HashMap; +//! +//! // `Path` gives you the path parameters and deserializes them. See its docs for +//! // more details +//! async fn path(Path(user_id): Path) {} +//! +//! // `Query` gives you the query parameters and deserializes them. +//! async fn query(Query(params): Query>) {} +//! +//! // `HeaderMap` gives you all the headers +//! async fn headers(headers: HeaderMap) {} +//! +//! // `String` consumes the request body and ensures it is valid utf-8 +//! async fn string(body: String) {} +//! +//! // `Bytes` gives you the raw request body +//! async fn bytes(body: Bytes) {} +//! +//! // We've already seen `Json` for parsing the request body as json +//! async fn json(Json(payload): Json) {} +//! +//! // `Request` gives you the whole request for maximum control +//! async fn request(request: Request) {} +//! +//! // `Extension` extracts data from "request extensions" +//! // This is commonly used to share state with handlers +//! async fn extension(Extension(state): Extension) {} +//! +//! #[derive(Clone)] +//! struct State { /* ... */ } +//! +//! let app = Router::new() +//! .route("/path/{user_id}", post(path)) +//! .route("/query", post(query)) +//! .route("/string", post(string)) +//! .route("/bytes", post(bytes)) +//! .route("/json", post(json)) +//! .route("/request", post(request)) +//! .route("/extension", post(extension)); +//! # let _: Router = app; +//! ``` +//! +//! # Applying multiple extractors +//! +//! You can also apply multiple extractors: +//! +//! ```rust,no_run +//! use axum::{ +//! extract::{Path, Query}, +//! routing::get, +//! Router, +//! }; +//! use uuid::Uuid; +//! use serde::Deserialize; +//! +//! let app = Router::new().route("/users/{id}/things", get(get_user_things)); +//! +//! #[derive(Deserialize)] +//! struct Pagination { +//! page: usize, +//! per_page: usize, +//! } +//! +//! async fn get_user_things( +//! Path(user_id): Path, +//! Query(pagination): Query, +//! ) { +//! // ... +//! } +//! # let _: Router = app; +//! ``` +//! +//! # The order of extractors +//! +//! Extractors always run in the order of the function parameters that is from +//! left to right. +//! +//! The request body is an asynchronous stream that can only be consumed once. +//! Therefore you can only have one extractor that consumes the request body. axum +//! enforces this by requiring such extractors to be the _last_ argument your +//! handler takes. +//! +//! For example +//! +//! ```rust +//! use axum::{extract::State, http::{Method, HeaderMap}}; +//! # +//! # #[derive(Clone)] +//! # struct AppState { +//! # } +//! +//! async fn handler( +//! // `Method` and `HeaderMap` don't consume the request body so they can +//! // put anywhere in the argument list (but before `body`) +//! method: Method, +//! headers: HeaderMap, +//! // `State` is also an extractor so it needs to be before `body` +//! State(state): State, +//! // `String` consumes the request body and thus must be the last extractor +//! body: String, +//! ) { +//! // ... +//! } +//! # +//! # let _: axum::routing::MethodRouter = axum::routing::get(handler); +//! ``` +//! +//! We get a compile error if `String` isn't the last extractor: +//! +//! ```rust,compile_fail +//! use axum::http::Method; +//! +//! async fn handler( +//! // this doesn't work since `String` must be the last argument +//! body: String, +//! method: Method, +//! ) { +//! // ... +//! } +//! # +//! # let _: axum::routing::MethodRouter = axum::routing::get(handler); +//! ``` +//! +//! This also means you cannot consume the request body twice: +//! +//! ```rust,compile_fail +//! use axum::Json; +//! use serde::Deserialize; +//! +//! #[derive(Deserialize)] +//! struct Payload {} +//! +//! async fn handler( +//! // `String` and `Json` both consume the request body +//! // so they cannot both be used +//! string_body: String, +//! json_body: Json, +//! ) { +//! // ... +//! } +//! # +//! # let _: axum::routing::MethodRouter = axum::routing::get(handler); +//! ``` +//! +//! axum enforces this by requiring the last extractor implements [`FromRequest`] +//! and all others implement [`FromRequestParts`]. +//! +//! # Handling extractor rejections +//! +//! If you want to handle the case of an extractor failing within a specific +//! handler, you can wrap it in `Result`, with the error being the rejection type +//! of the extractor: +//! +//! ```rust,no_run +//! use axum::{ +//! extract::{Json, rejection::JsonRejection}, +//! routing::post, +//! Router, +//! }; +//! use serde_json::Value; +//! +//! async fn create_user(payload: Result, JsonRejection>) { +//! match payload { +//! Ok(payload) => { +//! // We got a valid JSON payload +//! } +//! Err(JsonRejection::MissingJsonContentType(_)) => { +//! // Request didn't have `Content-Type: application/json` +//! // header +//! } +//! Err(JsonRejection::JsonDataError(_)) => { +//! // Couldn't deserialize the body into the target type +//! } +//! Err(JsonRejection::JsonSyntaxError(_)) => { +//! // Syntax error in the body +//! } +//! Err(JsonRejection::BytesRejection(_)) => { +//! // Failed to extract the request body +//! } +//! Err(_) => { +//! // `JsonRejection` is marked `#[non_exhaustive]` so match must +//! // include a catch-all case. +//! } +//! } +//! } +//! +//! let app = Router::new().route("/users", post(create_user)); +//! # let _: Router = app; +//! ``` +//! +//! # Optional extractors +//! +//! Some extractors implement [`OptionalFromRequestParts`] in addition to +//! [`FromRequestParts`], or [`OptionalFromRequest`] in addition to [`FromRequest`]. +//! +//! These extractors can be used inside of `Option`. It depends on the particular +//! `OptionalFromRequestParts` or `OptionalFromRequest` implementation what this +//! does: For example for `TypedHeader` from axum-extra, you get `None` if the +//! header you're trying to extract is not part of the request, but if the header +//! is present and fails to parse, the request is rejected. +//! +//! ```rust,no_run +//! use axum::{routing::post, Router}; +//! use axum_extra::{headers::UserAgent, TypedHeader}; +//! use serde_json::Value; +//! +//! async fn foo(user_agent: Option>) { +//! if let Some(TypedHeader(user_agent)) = user_agent { +//! // The client sent a user agent +//! } else { +//! // No user agent header +//! } +//! } +//! +//! let app = Router::new().route("/foo", post(foo)); +//! # let _: Router = app; +//! ``` +//! +//! # Customizing extractor responses +//! +//! If an extractor fails it will return a response with the error and your +//! handler will not be called. To customize the error response you have two +//! options: +//! +//! 1. Use `Result` as your extractor like shown in +//! ["Handling extractor rejections"](#handling-extractor-rejections). +//! This works well if you're only using the extractor in a single handler. +//! 2. Create your own extractor that in its [`FromRequest`] implementation calls +//! one of axum's built in extractors but returns a different response for +//! rejections. See the [customize-extractor-error] example for more details. +//! +//! # Accessing inner errors +//! +//! axum's built-in extractors don't directly expose the inner error. This gives us +//! more flexibility and allows us to change internal implementations without +//! breaking the public API. +//! +//! For example that means while [`Json`] is implemented using [`serde_json`] it +//! doesn't directly expose the [`serde_json::Error`] that's contained in +//! [`JsonRejection::JsonDataError`]. However it is still possible to access via +//! methods from [`std::error::Error`]: +//! +//! ```rust +//! use std::error::Error; +//! use axum::{ +//! extract::{Json, rejection::JsonRejection}, +//! response::IntoResponse, +//! http::StatusCode, +//! }; +//! use serde_json::{json, Value}; +//! +//! async fn handler( +//! result: Result, JsonRejection>, +//! ) -> Result, (StatusCode, String)> { +//! match result { +//! // if the client sent valid JSON then we're good +//! Ok(Json(payload)) => Ok(Json(json!({ "payload": payload }))), +//! +//! Err(err) => match err { +//! JsonRejection::JsonDataError(err) => { +//! Err(serde_json_error_response(err)) +//! } +//! JsonRejection::JsonSyntaxError(err) => { +//! Err(serde_json_error_response(err)) +//! } +//! // handle other rejections from the `Json` extractor +//! JsonRejection::MissingJsonContentType(_) => Err(( +//! StatusCode::BAD_REQUEST, +//! "Missing `Content-Type: application/json` header".to_string(), +//! )), +//! JsonRejection::BytesRejection(_) => Err(( +//! StatusCode::INTERNAL_SERVER_ERROR, +//! "Failed to buffer request body".to_string(), +//! )), +//! // we must provide a catch-all case since `JsonRejection` is marked +//! // `#[non_exhaustive]` +//! _ => Err(( +//! StatusCode::INTERNAL_SERVER_ERROR, +//! "Unknown error".to_string(), +//! )), +//! }, +//! } +//! } +//! +//! // attempt to extract the inner `serde_path_to_error::Error`, +//! // if that succeeds we can provide a more specific error. +//! // +//! // `Json` uses `serde_path_to_error` so the error will be wrapped in `serde_path_to_error::Error`. +//! fn serde_json_error_response(err: E) -> (StatusCode, String) +//! where +//! E: Error + 'static, +//! { +//! if let Some(err) = find_error_source::>(&err) { +//! let serde_json_err = err.inner(); +//! ( +//! StatusCode::BAD_REQUEST, +//! format!( +//! "Invalid JSON at line {} column {}", +//! serde_json_err.line(), +//! serde_json_err.column() +//! ), +//! ) +//! } else { +//! (StatusCode::BAD_REQUEST, "Unknown error".to_string()) +//! } +//! } +//! +//! // attempt to downcast `err` into a `T` and if that fails recursively try and +//! // downcast `err`'s source +//! fn find_error_source<'a, T>(err: &'a (dyn Error + 'static)) -> Option<&'a T> +//! where +//! T: Error + 'static, +//! { +//! if let Some(err) = err.downcast_ref::() { +//! Some(err) +//! } else if let Some(source) = err.source() { +//! find_error_source(source) +//! } else { +//! None +//! } +//! } +//! # +//! # #[tokio::main] +//! # async fn main() { +//! # use axum::extract::FromRequest; +//! # +//! # let req = axum::http::Request::builder() +//! # .header("content-type", "application/json") +//! # .body(axum::body::Body::from("{")) +//! # .unwrap(); +//! # +//! # let err = match Json::::from_request(req, &()).await.unwrap_err() { +//! # JsonRejection::JsonSyntaxError(err) => err, +//! # _ => panic!(), +//! # }; +//! # +//! # let (_, body) = serde_json_error_response(err); +//! # assert_eq!(body, "Invalid JSON at line 1 column 1"); +//! # } +//! ``` +//! +//! Note that while this approach works it might break in the future if axum changes +//! its implementation to use a different error type internally. Such changes might +//! happen without major breaking versions. +//! +//! # Defining custom extractors +//! +//! You can also define your own extractors by implementing either +//! [`FromRequestParts`] or [`FromRequest`]. +//! +//! ## Implementing `FromRequestParts` +//! +//! Implement `FromRequestParts` if your extractor doesn't need access to the +//! request body: +//! +//! ```rust,no_run +//! use axum::{ +//! extract::FromRequestParts, +//! routing::get, +//! Router, +//! http::{ +//! StatusCode, +//! header::{HeaderValue, USER_AGENT}, +//! request::Parts, +//! }, +//! }; +//! +//! struct ExtractUserAgent(HeaderValue); +//! +//! impl FromRequestParts for ExtractUserAgent +//! where +//! S: Send + Sync, +//! { +//! type Rejection = (StatusCode, &'static str); +//! +//! async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { +//! if let Some(user_agent) = parts.headers.get(USER_AGENT) { +//! Ok(ExtractUserAgent(user_agent.clone())) +//! } else { +//! Err((StatusCode::BAD_REQUEST, "`User-Agent` header is missing")) +//! } +//! } +//! } +//! +//! async fn handler(ExtractUserAgent(user_agent): ExtractUserAgent) { +//! // ... +//! } +//! +//! let app = Router::new().route("/foo", get(handler)); +//! # let _: Router = app; +//! ``` +//! +//! ## Implementing `FromRequest` +//! +//! If your extractor needs to consume the request body you must implement [`FromRequest`] +//! +//! ```rust,no_run +//! use axum::{ +//! extract::{Request, FromRequest}, +//! response::{Response, IntoResponse}, +//! body::{Bytes, Body}, +//! routing::get, +//! Router, +//! http::{ +//! StatusCode, +//! header::{HeaderValue, USER_AGENT}, +//! }, +//! }; +//! +//! struct ValidatedBody(Bytes); +//! +//! impl FromRequest for ValidatedBody +//! where +//! Bytes: FromRequest, +//! S: Send + Sync, +//! { +//! type Rejection = Response; +//! +//! async fn from_request(req: Request, state: &S) -> Result { +//! let body = Bytes::from_request(req, state) +//! .await +//! .map_err(IntoResponse::into_response)?; +//! +//! // do validation... +//! +//! Ok(Self(body)) +//! } +//! } +//! +//! async fn handler(ValidatedBody(body): ValidatedBody) { +//! // ... +//! } +//! +//! let app = Router::new().route("/foo", get(handler)); +//! # let _: Router = app; +//! ``` +//! +//! ## Cannot implement both `FromRequest` and `FromRequestParts` +//! +//! Note that you will make your extractor unusable by implementing both +//! `FromRequest` and `FromRequestParts` directly for the same type, unless it is +//! wrapping another extractor: +//! +//! ```rust,compile_fail +//! use axum::{ +//! Router, +//! routing::get, +//! extract::{FromRequest, Request, FromRequestParts}, +//! http::request::Parts, +//! body::Body, +//! }; +//! use std::convert::Infallible; +//! +//! // Some extractor that doesn't wrap another extractor +//! struct MyExtractor; +//! +//! // `MyExtractor` implements both `FromRequest` +//! impl FromRequest for MyExtractor +//! where +//! S: Send + Sync, +//! { +//! type Rejection = Infallible; +//! +//! async fn from_request(req: Request, state: &S) -> Result { +//! // ... +//! # todo!() +//! } +//! } +//! +//! // and `FromRequestParts` +//! impl FromRequestParts for MyExtractor +//! where +//! S: Send + Sync, +//! { +//! type Rejection = Infallible; +//! +//! async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { +//! // ... +//! # todo!() +//! } +//! } +//! +//! let app = Router::new().route( +//! "/", +//! // This fails when we go to actually use `MyExtractor` in a handler function. +//! // This is due to a limit in Rust's type system. +//! // +//! // The workaround is to implement either `FromRequest` or `FromRequestParts` +//! // but not both, if your extractor doesn't wrap another extractor. +//! // +//! // See "Wrapping extractors" for how to wrap other extractors. +//! get(|_: MyExtractor| async {}), +//! ); +//! # let _: Router = app; +//! ``` +//! +//! # Accessing other extractors in `FromRequest` or `FromRequestParts` implementations +//! +//! When defining custom extractors you often need to access another extractor +//! in your implementation. +//! +//! ```rust +//! use axum::{ +//! extract::{Extension, FromRequestParts}, +//! http::{StatusCode, HeaderMap, request::Parts}, +//! response::{IntoResponse, Response}, +//! routing::get, +//! Router, +//! }; +//! +//! #[derive(Clone)] +//! struct State { +//! // ... +//! } +//! +//! struct AuthenticatedUser { +//! // ... +//! } +//! +//! impl FromRequestParts for AuthenticatedUser +//! where +//! S: Send + Sync, +//! { +//! type Rejection = Response; +//! +//! async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { +//! // You can either call them directly... +//! let headers = HeaderMap::from_request_parts(parts, state) +//! .await +//! .map_err(|err| match err {})?; +//! +//! // ... or use `extract` / `extract_with_state` from `RequestExt` / `RequestPartsExt` +//! use axum::RequestPartsExt; +//! let Extension(state) = parts.extract::>() +//! .await +//! .map_err(|err| err.into_response())?; +//! +//! unimplemented!("actually perform the authorization") +//! } +//! } +//! +//! async fn handler(user: AuthenticatedUser) { +//! // ... +//! } +//! +//! let state = State { /* ... */ }; +//! +//! let app = Router::new().route("/", get(handler)).layer(Extension(state)); +//! # let _: Router = app; +//! ``` +//! +//! # Request body limits +//! +//! For security reasons, [`Bytes`] will, by default, not accept bodies larger than +//! 2MB. This also applies to extractors that uses [`Bytes`] internally such as +//! `String`, [`Json`], and [`Form`]. +//! +//! For more details, including how to disable this limit, see [`DefaultBodyLimit`]. +//! +//! # Wrapping extractors +//! +//! If you want write an extractor that generically wraps another extractor (that +//! may or may not consume the request body) you should implement both +//! [`FromRequest`] and [`FromRequestParts`]: +//! +//! ```rust +//! use axum::{ +//! Router, +//! body::Body, +//! routing::get, +//! extract::{Request, FromRequest, FromRequestParts}, +//! http::{HeaderMap, request::Parts}, +//! }; +//! use std::time::{Instant, Duration}; +//! +//! // an extractor that wraps another and measures how long time it takes to run +//! struct Timing { +//! extractor: E, +//! duration: Duration, +//! } +//! +//! // we must implement both `FromRequestParts` +//! impl FromRequestParts for Timing +//! where +//! S: Send + Sync, +//! T: FromRequestParts, +//! { +//! type Rejection = T::Rejection; +//! +//! async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { +//! let start = Instant::now(); +//! let extractor = T::from_request_parts(parts, state).await?; +//! let duration = start.elapsed(); +//! Ok(Timing { +//! extractor, +//! duration, +//! }) +//! } +//! } +//! +//! // and `FromRequest` +//! impl FromRequest for Timing +//! where +//! S: Send + Sync, +//! T: FromRequest, +//! { +//! type Rejection = T::Rejection; +//! +//! async fn from_request(req: Request, state: &S) -> Result { +//! let start = Instant::now(); +//! let extractor = T::from_request(req, state).await?; +//! let duration = start.elapsed(); +//! Ok(Timing { +//! extractor, +//! duration, +//! }) +//! } +//! } +//! +//! async fn handler( +//! // this uses the `FromRequestParts` impl +//! _: Timing, +//! // this uses the `FromRequest` impl +//! _: Timing, +//! ) {} +//! # let _: axum::routing::MethodRouter = axum::routing::get(handler); +//! ``` +//! +//! # Logging rejections +//! +//! All built-in extractors will log rejections for easier debugging. To see the +//! logs, enable the `tracing` feature for axum (enabled by default) and the +//! `axum::rejection=trace` tracing target, for example with +//! `RUST_LOG=info,axum::rejection=trace cargo run`. +//! +//! [axum-extra]: https://docs.rs/axum-extra/latest/axum_extra/extract/index.html +//! [`body::Body`]: crate::body::Body +//! [`Bytes`]: crate::body::Bytes +//! [customize-extractor-error]: https://github.com/tokio-rs/axum/blob/main/examples/customize-extractor-error/src/main.rs +//! [`HeaderMap`]: https://docs.rs/http/latest/http/header/struct.HeaderMap.html +//! [`Request`]: https://docs.rs/http/latest/http/struct.Request.html +//! [`JsonRejection::JsonDataError`]: rejection::JsonRejection::JsonDataError use http::header::{self, HeaderMap}; diff --git a/axum/src/handler/mod.rs b/axum/src/handler/mod.rs index e99be5f8..cc4bde01 100644 --- a/axum/src/handler/mod.rs +++ b/axum/src/handler/mod.rs @@ -1,6 +1,11 @@ //! Async functions that can be used to handle requests. //! -#![doc = include_str!("../docs/handlers_intro.md")] +//! In axum a "handler" is an async function that accepts zero or more +//! ["extractors"](crate::extract) as arguments and returns something that +//! can be converted [into a response](crate::response). +//! +//! Handlers are where your application logic lives and axum applications are built +//! by routing between handlers. //! //! Some examples of handlers: //! @@ -42,7 +47,45 @@ //! [anyhow]: https://github.com/tokio-rs/axum/blob/main/examples/anyhow-error-response/src/main.rs //! [error-handling]: https://github.com/tokio-rs/axum/blob/main/examples/error-handling/src/main.rs //! -#![doc = include_str!("../docs/debugging_handler_type_errors.md")] +//! ## Debugging handler type errors +//! +//! For a function to be used as a handler it must implement the [`Handler`] trait. +//! axum provides blanket implementations for functions that: +//! +//! - Are `async fn`s. +//! - Take no more than 16 arguments that all implement `Send`. +//! - All except the last argument implement [`FromRequestParts`]. +//! - The last argument implements [`FromRequest`]. +//! - Returns something that implements [`IntoResponse`]. +//! - If a closure is used it must implement `Clone + Send` and be +//! `'static`. +//! - Returns a future that is `Send`. The most common way to accidentally make a +//! future `!Send` is to hold a `!Send` type across an await. +//! +//! Unfortunately Rust gives poor error messages if you try to use a function +//! that doesn't quite match what's required by [`Handler`]. +//! +//! You might get an error like this: +//! +//! ```not_rust +//! error[E0277]: the trait bound `fn(bool) -> impl Future {handler}: Handler<_, _>` is not satisfied +//! --> src/main.rs:13:44 +//! | +//! 13 | let app = Router::new().route("/", get(handler)); +//! | ^^^^^^^ the trait `Handler<_, _>` is not implemented for `fn(bool) -> impl Future {handler}` +//! | +//! ::: axum/src/handler/mod.rs:116:8 +//! | +//! 116 | H: Handler, +//! | ------------- required by this bound in `axum::routing::get` +//! ``` +//! +//! This error doesn't tell you _why_ your function doesn't implement +//! [`Handler`]. It's possible to improve the error with the [`debug_handler`] +//! proc-macro from the [axum-macros] crate. +//! +//! [axum-macros]: https://docs.rs/axum-macros +//! [`debug_handler`]: https://docs.rs/axum-macros/latest/axum_macros/attr.debug_handler.html #[cfg(feature = "tokio")] use crate::extract::connect_info::IntoMakeServiceWithConnectInfo; @@ -99,7 +142,46 @@ pub use self::service::HandlerService; /// S: Service, /// {} /// ``` -#[doc = include_str!("../docs/debugging_handler_type_errors.md")] +/// +/// ## Debugging handler type errors +/// +/// For a function to be used as a handler it must implement the [`Handler`] trait. +/// axum provides blanket implementations for functions that: +/// +/// - Are `async fn`s. +/// - Take no more than 16 arguments that all implement `Send`. +/// - All except the last argument implement [`FromRequestParts`]. +/// - The last argument implements [`FromRequest`]. +/// - Returns something that implements [`IntoResponse`]. +/// - If a closure is used it must implement `Clone + Send` and be +/// `'static`. +/// - Returns a future that is `Send`. The most common way to accidentally make a +/// future `!Send` is to hold a `!Send` type across an await. +/// +/// Unfortunately Rust gives poor error messages if you try to use a function +/// that doesn't quite match what's required by [`Handler`]. +/// +/// You might get an error like this: +/// +/// ```not_rust +/// error[E0277]: the trait bound `fn(bool) -> impl Future {handler}: Handler<_, _>` is not satisfied +/// --> src/main.rs:13:44 +/// | +/// 13 | let app = Router::new().route("/", get(handler)); +/// | ^^^^^^^ the trait `Handler<_, _>` is not implemented for `fn(bool) -> impl Future {handler}` +/// | +/// ::: axum/src/handler/mod.rs:116:8 +/// | +/// 116 | H: Handler, +/// | ------------- required by this bound in `axum::routing::get` +/// ``` +/// +/// This error doesn't tell you _why_ your function doesn't implement +/// [`Handler`]. It's possible to improve the error with the [`debug_handler`] +/// proc-macro from the [axum-macros] crate. +/// +/// [axum-macros]: https://docs.rs/axum-macros +/// [`debug_handler`]: crate::debug_handler /// /// # Handlers that aren't functions /// diff --git a/axum/src/lib.rs b/axum/src/lib.rs index 03301667..95e40946 100644 --- a/axum/src/lib.rs +++ b/axum/src/lib.rs @@ -69,9 +69,14 @@ //! //! # Handlers //! -#![doc = include_str!("docs/handlers_intro.md")] +//! In axum a "handler" is an async function that accepts zero or more +//! ["extractors"](crate::extract) as arguments and returns something that +//! can be converted [into a response](crate::response). //! -//! See [`handler`](crate::handler) for more details on handlers. +//! Handlers are where your application logic lives and axum applications are built +//! by routing between handlers. +//! +//! See [`handler`] for more details on handlers. //! //! # Extractors //! @@ -94,7 +99,7 @@ //! async fn json(Json(payload): Json) {} //! ``` //! -//! See [`extract`](crate::extract) for more details on extractors. +//! See [`extract`] for more details on extractors. //! //! # Responses //! @@ -126,7 +131,7 @@ //! # let _: Router = app; //! ``` //! -//! See [`response`](crate::response) for more details on building responses. +//! See [`response`] for more details on building responses. //! //! # Error handling //! diff --git a/axum/src/middleware/mod.rs b/axum/src/middleware/mod.rs index 41b7671c..0fd18ffe 100644 --- a/axum/src/middleware/mod.rs +++ b/axum/src/middleware/mod.rs @@ -1,6 +1,588 @@ //! Utilities for writing middleware //! -#![doc = include_str!("../docs/middleware.md")] +//! # Intro +//! +//! axum is unique in that it doesn't have its own bespoke middleware system and +//! instead integrates with [`tower`]. This means the ecosystem of [`tower`] and +//! [`tower-http`] middleware all work with axum. +//! +//! While it's not necessary to fully understand tower to write or use middleware +//! with axum, having at least a basic understanding of tower's concepts is +//! recommended. See [tower's guides][tower-guides] for a general introduction. +//! Reading the documentation for [`tower::ServiceBuilder`] is also recommended. +//! +//! # Applying middleware +//! +//! axum allows you to add middleware just about anywhere +//! +//! - To entire routers with [`Router::layer`] and [`Router::route_layer`]. +//! - To method routers with [`MethodRouter::layer`] and [`MethodRouter::route_layer`]. +//! - To individual handlers with [`Handler::layer`]. +//! +//! ## Applying multiple middleware +//! +//! It's recommended to use [`tower::ServiceBuilder`] to apply multiple middleware at +//! once, instead of calling `layer` (or `route_layer`) repeatedly: +//! +//! ```rust +//! use axum::{ +//! routing::get, +//! Extension, +//! Router, +//! }; +//! use tower_http::{trace::TraceLayer}; +//! use tower::ServiceBuilder; +//! +//! async fn handler() {} +//! +//! #[derive(Clone)] +//! struct State {} +//! +//! let app = Router::new() +//! .route("/", get(handler)) +//! .layer( +//! ServiceBuilder::new() +//! .layer(TraceLayer::new_for_http()) +//! .layer(Extension(State {})) +//! ); +//! # let _: Router = app; +//! ``` +//! +//! # Commonly used middleware +//! +//! Some commonly used middleware are: +//! +//! - [`TraceLayer`](tower_http::trace) for high level tracing/logging. +//! - [`CorsLayer`](tower_http::cors) for handling CORS. +//! - [`CompressionLayer`](tower_http::compression) for automatic compression of responses. +//! - [`RequestIdLayer`](tower_http::request_id) and +//! [`PropagateRequestIdLayer`](tower_http::request_id) set and propagate request +//! ids. +//! - [`TimeoutLayer`](tower_http::timeout::TimeoutLayer) for timeouts. +//! +//! # Ordering +//! +//! When you add middleware with [`Router::layer`] (or similar) all previously added +//! routes will be wrapped in the middleware. Generally speaking, this results in +//! middleware being executed from bottom to top. +//! +//! So if you do this: +//! +//! ```rust +//! use axum::{routing::get, Router}; +//! +//! async fn handler() {} +//! +//! # let layer_one = axum::Extension(()); +//! # let layer_two = axum::Extension(()); +//! # let layer_three = axum::Extension(()); +//! # +//! let app = Router::new() +//! .route("/", get(handler)) +//! .layer(layer_one) +//! .layer(layer_two) +//! .layer(layer_three); +//! # let _: Router = app; +//! ``` +//! +//! Think of the middleware as being layered like an onion where each new layer +//! wraps all previous layers: +//! +//! ```not_rust +//! requests +//! | +//! v +//! +----- layer_three -----+ +//! | +---- layer_two ----+ | +//! | | +-- layer_one --+ | | +//! | | | | | | +//! | | | handler | | | +//! | | | | | | +//! | | +-- layer_one --+ | | +//! | +---- layer_two ----+ | +//! +----- layer_three -----+ +//! | +//! v +//! responses +//! ``` +//! +//! That is: +//! +//! - First `layer_three` receives the request +//! - It then does its thing and passes the request onto `layer_two` +//! - Which passes the request onto `layer_one` +//! - Which passes the request onto `handler` where a response is produced +//! - That response is then passed to `layer_one` +//! - Then to `layer_two` +//! - And finally to `layer_three` where it's returned out of your app +//! +//! It's a little more complicated in practice because any middleware is free to +//! return early and not call the next layer, for example if a request cannot be +//! authorized, but it's a useful mental model to have. +//! +//! As previously mentioned it's recommended to add multiple middleware using +//! `tower::ServiceBuilder`, however this impacts ordering: +//! +//! ```rust +//! use tower::ServiceBuilder; +//! use axum::{routing::get, Router}; +//! +//! async fn handler() {} +//! +//! # let layer_one = axum::Extension(()); +//! # let layer_two = axum::Extension(()); +//! # let layer_three = axum::Extension(()); +//! # +//! let app = Router::new() +//! .route("/", get(handler)) +//! .layer( +//! ServiceBuilder::new() +//! .layer(layer_one) +//! .layer(layer_two) +//! .layer(layer_three), +//! ); +//! # let _: Router = app; +//! ``` +//! +//! `ServiceBuilder` works by composing all layers into one such that they run top +//! to bottom. So with the previous code `layer_one` would receive the request +//! first, then `layer_two`, then `layer_three`, then `handler`, and then the +//! response would bubble back up through `layer_three`, then `layer_two`, and +//! finally `layer_one`. +//! +//! Executing middleware top to bottom is generally easier to understand and follow +//! mentally which is one of the reasons `ServiceBuilder` is recommended. +//! +//! # Writing middleware +//! +//! axum offers many ways of writing middleware, at different levels of abstraction +//! and with different pros and cons. +//! +//! ## `axum::middleware::from_fn` +//! +//! Use [`axum::middleware::from_fn`] to write your middleware when: +//! +//! - You're not comfortable with implementing your own futures and would rather use +//! the familiar `async`/`await` syntax. +//! - You don't intend to publish your middleware as a crate for others to use. +//! Middleware written like this are only compatible with axum. +//! +//! ## `axum::middleware::from_extractor` +//! +//! Use [`axum::middleware::from_extractor`] to write your middleware when: +//! +//! - You have a type that you sometimes want to use as an extractor and sometimes +//! as a middleware. If you only need your type as a middleware prefer +//! [`middleware::from_fn`]. +//! +//! ## tower's combinators +//! +//! tower has several utility combinators that can be used to perform simple +//! modifications to requests or responses. The most commonly used ones are +//! +//! - [`ServiceBuilder::map_request`] +//! - [`ServiceBuilder::map_response`] +//! - [`ServiceBuilder::then`] +//! - [`ServiceBuilder::and_then`] +//! +//! You should use these when +//! +//! - You want to perform a small ad hoc operation, such as adding a header. +//! - You don't intend to publish your middleware as a crate for others to use. +//! +//! ## `tower::Service` and `Pin>` +//! +//! For maximum control (and a more low level API) you can write your own middleware +//! by implementing [`tower::Service`]: +//! +//! Use [`tower::Service`] with `Pin>` to write your middleware when: +//! +//! - Your middleware needs to be configurable for example via builder methods on +//! your [`tower::Layer`] such as [`tower_http::trace::TraceLayer`]. +//! - You do intend to publish your middleware as a crate for others to use. +//! - You're not comfortable with implementing your own futures. +//! +//! A decent template for such a middleware could be: +//! +//! ```rust +//! use axum::{ +//! response::Response, +//! body::Body, +//! extract::Request, +//! }; +//! use futures_core::future::BoxFuture; +//! use tower::{Service, Layer}; +//! use std::task::{Context, Poll}; +//! +//! #[derive(Clone)] +//! struct MyLayer; +//! +//! impl Layer for MyLayer { +//! type Service = MyMiddleware; +//! +//! fn layer(&self, inner: S) -> Self::Service { +//! MyMiddleware { inner } +//! } +//! } +//! +//! #[derive(Clone)] +//! struct MyMiddleware { +//! inner: S, +//! } +//! +//! impl Service for MyMiddleware +//! where +//! S: Service + Send + 'static, +//! S::Future: Send + 'static, +//! { +//! type Response = S::Response; +//! type Error = S::Error; +//! // `BoxFuture` is a type alias for `Pin>` +//! type Future = BoxFuture<'static, Result>; +//! +//! fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { +//! self.inner.poll_ready(cx) +//! } +//! +//! fn call(&mut self, request: Request) -> Self::Future { +//! let future = self.inner.call(request); +//! Box::pin(async move { +//! let response: Response = future.await?; +//! Ok(response) +//! }) +//! } +//! } +//! ``` +//! +//! Note that your error type being defined as `S::Error` means that your middleware typically _returns no errors_. As a principle always try to return a response and try not to bail out with a custom error type. For example, if a 3rd party library you are using inside your new middleware returns its own specialized error type, try to convert it to some reasonable response and return `Ok` with that response. +//! +//! If you choose to implement a custom error type such as `type Error = BoxError` (a boxed opaque error), or any other error type that is not `Infallible`, you must use a `HandleErrorLayer`, here is an example using a `ServiceBuilder`: +//! +//! ```ignore +//! ServiceBuilder::new() +//! .layer(HandleErrorLayer::new(|_: BoxError| async { +//! // because axum uses infallible errors, you must handle your custom error type from your middleware here +//! StatusCode::BAD_REQUEST +//! })) +//! .layer( +//! // +//! ); +//! ``` +//! +//! ## `tower::Service` and custom futures +//! +//! If you're comfortable implementing your own futures (or want to learn it) and +//! need as much control as possible then using `tower::Service` without boxed +//! futures is the way to go. +//! +//! Use [`tower::Service`] with manual futures to write your middleware when: +//! +//! - You want your middleware to have the lowest possible overhead. +//! - Your middleware needs to be configurable for example via builder methods on +//! your [`tower::Layer`] such as [`tower_http::trace::TraceLayer`]. +//! - You do intend to publish your middleware as a crate for others to use, perhaps +//! as part of tower-http. +//! - You're comfortable with implementing your own futures, or want to learn how +//! the lower levels of async Rust works. +//! +//! tower's ["Building a middleware from scratch"][tower-from-scratch-guide] +//! guide is a good place to learn how to do this. +//! +//! # Error handling for middleware +//! +//! axum's error handling model requires handlers to always return a response. +//! However middleware is one possible way to introduce errors into an application. +//! If hyper receives an error the connection will be closed without sending a +//! response. Thus axum requires those errors to be handled gracefully: +//! +//! ```rust +//! use axum::{ +//! routing::get, +//! error_handling::HandleErrorLayer, +//! http::StatusCode, +//! BoxError, +//! Router, +//! }; +//! use tower::{ServiceBuilder, timeout::TimeoutLayer}; +//! use std::time::Duration; +//! +//! async fn handler() {} +//! +//! let app = Router::new() +//! .route("/", get(handler)) +//! .layer( +//! ServiceBuilder::new() +//! // this middleware goes above `TimeoutLayer` because it will receive +//! // errors returned by `TimeoutLayer` +//! .layer(HandleErrorLayer::new(|_: BoxError| async { +//! StatusCode::REQUEST_TIMEOUT +//! })) +//! .layer(TimeoutLayer::new(Duration::from_secs(10))) +//! ); +//! # let _: Router = app; +//! ``` +//! +//! See [`error_handling`](crate::error_handling) for more details on axum's error +//! handling model. +//! +//! # Routing to services/middleware and backpressure +//! +//! Generally routing to one of multiple services and backpressure doesn't mix +//! well. Ideally you would want ensure a service is ready to receive a request +//! before calling it. However, in order to know which service to call, you need +//! the request... +//! +//! One approach is to not consider the router service itself ready until all +//! destination services are ready. That is the approach used by +//! [`tower::steer::Steer`]. +//! +//! Another approach is to always consider all services ready (always return +//! `Poll::Ready(Ok(()))`) from `Service::poll_ready` and then actually drive +//! readiness inside the response future returned by `Service::call`. This works +//! well when your services don't care about backpressure and are always ready +//! anyway. +//! +//! axum expects that all services used in your app won't care about +//! backpressure and so it uses the latter strategy. However that means you +//! should avoid routing to a service (or using a middleware) that _does_ care +//! about backpressure. At the very least you should [load shed][tower::load_shed] +//! so requests are dropped quickly and don't keep piling up. +//! +//! It also means that if `poll_ready` returns an error then that error will be +//! returned in the response future from `call` and _not_ from `poll_ready`. In +//! that case, the underlying service will _not_ be discarded and will continue +//! to be used for future requests. Services that expect to be discarded if +//! `poll_ready` fails should _not_ be used with axum. +//! +//! One possible approach is to only apply backpressure sensitive middleware +//! around your entire app. This is possible because axum applications are +//! themselves services: +//! +//! ```rust +//! use axum::{ +//! routing::get, +//! Router, +//! }; +//! use tower::ServiceBuilder; +//! # let some_backpressure_sensitive_middleware = +//! # tower::layer::util::Identity::new(); +//! +//! async fn handler() { /* ... */ } +//! +//! let app = Router::new().route("/", get(handler)); +//! +//! let app = ServiceBuilder::new() +//! .layer(some_backpressure_sensitive_middleware) +//! .service(app); +//! # let _: Router = app; +//! ``` +//! +//! However when applying middleware around your whole application in this way +//! you have to take care that errors are still being handled appropriately. +//! +//! Also note that handlers created from async functions don't care about +//! backpressure and are always ready. So if you're not using any Tower +//! middleware you don't have to worry about any of this. +//! +//! # Accessing state in middleware +//! +//! How to make state available to middleware depends on how the middleware is +//! written. +//! +//! ## Accessing state in `axum::middleware::from_fn` +//! +//! Use [`axum::middleware::from_fn_with_state`](crate::middleware::from_fn_with_state). +//! +//! ## Accessing state in custom `tower::Layer`s +//! +//! ```rust +//! use axum::{ +//! Router, +//! routing::get, +//! middleware::{self, Next}, +//! response::Response, +//! extract::{State, Request}, +//! }; +//! use tower::{Layer, Service}; +//! use std::task::{Context, Poll}; +//! +//! #[derive(Clone)] +//! struct AppState {} +//! +//! #[derive(Clone)] +//! struct MyLayer { +//! state: AppState, +//! } +//! +//! impl Layer for MyLayer { +//! type Service = MyService; +//! +//! fn layer(&self, inner: S) -> Self::Service { +//! MyService { +//! inner, +//! state: self.state.clone(), +//! } +//! } +//! } +//! +//! #[derive(Clone)] +//! struct MyService { +//! inner: S, +//! state: AppState, +//! } +//! +//! impl Service> for MyService +//! where +//! S: Service>, +//! { +//! type Response = S::Response; +//! type Error = S::Error; +//! type Future = S::Future; +//! +//! fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { +//! self.inner.poll_ready(cx) +//! } +//! +//! fn call(&mut self, req: Request) -> Self::Future { +//! // Do something with `self.state`. +//! // +//! // See `axum::RequestExt` for how to run extractors directly from +//! // a `Request`. +//! +//! self.inner.call(req) +//! } +//! } +//! +//! async fn handler(_: State) {} +//! +//! let state = AppState {}; +//! +//! let app = Router::new() +//! .route("/", get(handler)) +//! .layer(MyLayer { state: state.clone() }) +//! .with_state(state); +//! # let _: axum::Router = app; +//! ``` +//! +//! # Passing state from middleware to handlers +//! +//! State can be passed from middleware to handlers using [request extensions]: +//! +//! ```rust +//! use axum::{ +//! Router, +//! http::StatusCode, +//! routing::get, +//! response::{IntoResponse, Response}, +//! middleware::{self, Next}, +//! extract::{Request, Extension}, +//! }; +//! +//! #[derive(Clone)] +//! struct CurrentUser { /* ... */ } +//! +//! async fn auth(mut req: Request, next: Next) -> Result { +//! let auth_header = req.headers() +//! .get(http::header::AUTHORIZATION) +//! .and_then(|header| header.to_str().ok()); +//! +//! let auth_header = if let Some(auth_header) = auth_header { +//! auth_header +//! } else { +//! return Err(StatusCode::UNAUTHORIZED); +//! }; +//! +//! if let Some(current_user) = authorize_current_user(auth_header).await { +//! // insert the current user into a request extension so the handler can +//! // extract it +//! req.extensions_mut().insert(current_user); +//! Ok(next.run(req).await) +//! } else { +//! Err(StatusCode::UNAUTHORIZED) +//! } +//! } +//! +//! async fn authorize_current_user(auth_token: &str) -> Option { +//! // ... +//! # unimplemented!() +//! } +//! +//! async fn handler( +//! // extract the current user, set by the middleware +//! Extension(current_user): Extension, +//! ) { +//! // ... +//! } +//! +//! let app = Router::new() +//! .route("/", get(handler)) +//! .route_layer(middleware::from_fn(auth)); +//! # let _: Router = app; +//! ``` +//! +//! [Response extensions] can also be used but note that request extensions are not +//! automatically moved to response extensions. You need to manually do that for the +//! extensions you need. +//! +//! # Rewriting request URI in middleware +//! +//! Middleware added with [`Router::layer`] will run after routing. That means it +//! cannot be used to run middleware that rewrites the request URI. By the time the +//! middleware runs the routing is already done. +//! +//! The workaround is to wrap the middleware around the entire `Router` (this works +//! because `Router` implements [`Service`]): +//! +//! ```rust +//! use tower::Layer; +//! use axum::{ +//! Router, +//! ServiceExt, // for `into_make_service` +//! response::Response, +//! middleware::Next, +//! extract::Request, +//! }; +//! +//! fn rewrite_request_uri(req: Request) -> Request { +//! // ... +//! # req +//! } +//! +//! // this can be any `tower::Layer` +//! let middleware = tower::util::MapRequestLayer::new(rewrite_request_uri); +//! +//! let app = Router::new(); +//! +//! // apply the layer around the whole `Router` +//! // this way the middleware will run before `Router` receives the request +//! let app_with_middleware = middleware.layer(app); +//! +//! # async { +//! let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); +//! axum::serve(listener, app_with_middleware.into_make_service()).await.unwrap(); +//! # }; +//! ``` +//! +//! [`tower`]: https://crates.io/crates/tower +//! [`tower-http`]: https://crates.io/crates/tower-http +//! [tower-guides]: https://github.com/tower-rs/tower/tree/master/guides +//! [`axum::middleware::from_fn`]: fn@crate::middleware::from_fn +//! [`middleware::from_fn`]: fn@crate::middleware::from_fn +//! [tower-from-scratch-guide]: https://github.com/tower-rs/tower/blob/master/guides/building-a-middleware-from-scratch.md +//! [`ServiceBuilder::map_request`]: tower::ServiceBuilder::map_request +//! [`ServiceBuilder::map_response`]: tower::ServiceBuilder::map_response +//! [`ServiceBuilder::then`]: tower::ServiceBuilder::then +//! [`ServiceBuilder::and_then`]: tower::ServiceBuilder::and_then +//! [`axum::middleware::from_extractor`]: fn@crate::middleware::from_extractor +//! [`Handler::layer`]: crate::handler::Handler::layer +//! [`Router::layer`]: crate::routing::Router::layer +//! [`MethodRouter::layer`]: crate::routing::MethodRouter::layer +//! [`Router::route_layer`]: crate::routing::Router::route_layer +//! [`MethodRouter::route_layer`]: crate::routing::MethodRouter::route_layer +//! [request extensions]: https://docs.rs/http/latest/http/request/struct.Request.html#method.extensions +//! [Response extensions]: https://docs.rs/http/latest/http/response/struct.Response.html#method.extensions +//! [`State`]: crate::extract::State +//! [`Service`]: tower::Service mod from_extractor; mod from_fn; diff --git a/axum/src/response/mod.rs b/axum/src/response/mod.rs index 70be7452..ef1c9deb 100644 --- a/axum/src/response/mod.rs +++ b/axum/src/response/mod.rs @@ -1,4 +1,324 @@ -#![doc = include_str!("../docs/response.md")] +//! Types and traits for generating responses. +//! +//! # Building responses +//! +//! Anything that implements [`IntoResponse`] can be returned from a handler. axum +//! provides implementations for common types: +//! +//! ```rust,no_run +//! use axum::{ +//! Json, +//! response::{Html, IntoResponse}, +//! http::{StatusCode, Uri, header::{self, HeaderMap, HeaderName}}, +//! }; +//! +//! // `()` gives an empty response +//! async fn empty() {} +//! +//! // String will get a `text/plain; charset=utf-8` content-type +//! async fn plain_text(uri: Uri) -> String { +//! format!("Hi from {}", uri.path()) +//! } +//! +//! // Bytes will get a `application/octet-stream` content-type +//! async fn bytes() -> Vec { +//! vec![1, 2, 3, 4] +//! } +//! +//! // `Json` will get a `application/json` content-type and work with anything that +//! // implements `serde::Serialize` +//! async fn json() -> Json> { +//! Json(vec!["foo".to_owned(), "bar".to_owned()]) +//! } +//! +//! // `Html` will get a `text/html` content-type +//! async fn html() -> Html<&'static str> { +//! Html("

Hello, World!

") +//! } +//! +//! // `StatusCode` gives an empty response with that status code +//! async fn status() -> StatusCode { +//! StatusCode::NOT_FOUND +//! } +//! +//! // `HeaderMap` gives an empty response with some headers +//! async fn headers() -> HeaderMap { +//! let mut headers = HeaderMap::new(); +//! headers.insert(header::SERVER, "axum".parse().unwrap()); +//! headers +//! } +//! +//! // An array of tuples also gives headers +//! async fn array_headers() -> [(HeaderName, &'static str); 2] { +//! [ +//! (header::SERVER, "axum"), +//! (header::CONTENT_TYPE, "text/plain") +//! ] +//! } +//! +//! // Use `impl IntoResponse` to avoid writing the whole type +//! async fn impl_trait() -> impl IntoResponse { +//! [ +//! (header::SERVER, "axum"), +//! (header::CONTENT_TYPE, "text/plain") +//! ] +//! } +//! ``` +//! +//! Additionally you can return tuples to build more complex responses from +//! individual parts. +//! +//! ```rust,no_run +//! use axum::{ +//! Json, +//! response::IntoResponse, +//! http::{StatusCode, HeaderMap, Uri, header}, +//! extract::Extension, +//! }; +//! +//! // `(StatusCode, impl IntoResponse)` will override the status code of the response +//! async fn with_status(uri: Uri) -> (StatusCode, String) { +//! (StatusCode::NOT_FOUND, format!("Not Found: {}", uri.path())) +//! } +//! +//! // Use `impl IntoResponse` to avoid having to type the whole type +//! async fn impl_trait(uri: Uri) -> impl IntoResponse { +//! (StatusCode::NOT_FOUND, format!("Not Found: {}", uri.path())) +//! } +//! +//! // `(HeaderMap, impl IntoResponse)` to add additional headers +//! async fn with_headers() -> impl IntoResponse { +//! let mut headers = HeaderMap::new(); +//! headers.insert(header::CONTENT_TYPE, "text/plain".parse().unwrap()); +//! (headers, "foo") +//! } +//! +//! // Or an array of tuples to more easily build the headers +//! async fn with_array_headers() -> impl IntoResponse { +//! ([(header::CONTENT_TYPE, "text/plain")], "foo") +//! } +//! +//! // Use string keys for custom headers +//! async fn with_array_headers_custom() -> impl IntoResponse { +//! ([("x-custom", "custom")], "foo") +//! } +//! +//! // `(StatusCode, headers, impl IntoResponse)` to set status and add headers +//! // `headers` can be either a `HeaderMap` or an array of tuples +//! async fn with_status_and_array_headers() -> impl IntoResponse { +//! ( +//! StatusCode::NOT_FOUND, +//! [(header::CONTENT_TYPE, "text/plain")], +//! "foo", +//! ) +//! } +//! +//! // `(Extension<_>, impl IntoResponse)` to set response extensions +//! async fn with_status_extensions() -> impl IntoResponse { +//! ( +//! Extension(Foo("foo")), +//! "foo", +//! ) +//! } +//! +//! #[derive(Clone)] +//! struct Foo(&'static str); +//! +//! // Or mix and match all the things +//! async fn all_the_things(uri: Uri) -> impl IntoResponse { +//! let mut header_map = HeaderMap::new(); +//! if uri.path() == "/" { +//! header_map.insert(header::SERVER, "axum".parse().unwrap()); +//! } +//! +//! ( +//! // set status code +//! StatusCode::NOT_FOUND, +//! // headers with an array +//! [("x-custom", "custom")], +//! // some extensions +//! Extension(Foo("foo")), +//! Extension(Foo("bar")), +//! // more headers, built dynamically +//! header_map, +//! // and finally the body +//! "foo", +//! ) +//! } +//! ``` +//! +//! In general you can return tuples like: +//! +//! - `(StatusCode, impl IntoResponse)` +//! - `(Parts, impl IntoResponse)` +//! - `(Response<()>, impl IntoResponse)` +//! - `(T1, .., Tn, impl IntoResponse)` where `T1` to `Tn` all implement [`IntoResponseParts`]. +//! - `(StatusCode, T1, .., Tn, impl IntoResponse)` where `T1` to `Tn` all implement [`IntoResponseParts`]. +//! - `(Parts, T1, .., Tn, impl IntoResponse)` where `T1` to `Tn` all implement [`IntoResponseParts`]. +//! - `(Response<()>, T1, .., Tn, impl IntoResponse)` where `T1` to `Tn` all implement [`IntoResponseParts`]. +//! +//! This means you cannot accidentally override the status or body as [`IntoResponseParts`] only allows +//! setting headers and extensions. +//! +//! Use [`Response`] for more low level control: +//! +//! ```rust,no_run +//! use axum::{ +//! Json, +//! response::{IntoResponse, Response}, +//! body::Body, +//! http::StatusCode, +//! }; +//! +//! async fn response() -> Response { +//! Response::builder() +//! .status(StatusCode::NOT_FOUND) +//! .header("x-foo", "custom header") +//! .body(Body::from("not found")) +//! .unwrap() +//! } +//! ``` +//! +//! # Returning different response types +//! +//! If you need to return multiple response types, and `Result` isn't appropriate, you can call +//! `.into_response()` to turn things into `axum::response::Response`: +//! +//! ```rust +//! use axum::{ +//! response::{IntoResponse, Redirect, Response}, +//! http::StatusCode, +//! }; +//! +//! async fn handle() -> Response { +//! if something() { +//! "All good!".into_response() +//! } else if something_else() { +//! ( +//! StatusCode::INTERNAL_SERVER_ERROR, +//! "Something went wrong...", +//! ).into_response() +//! } else { +//! Redirect::to("/").into_response() +//! } +//! } +//! +//! fn something() -> bool { +//! // ... +//! # true +//! } +//! +//! fn something_else() -> bool { +//! // ... +//! # true +//! } +//! ``` +//! +//! # Regarding `impl IntoResponse` +//! +//! You can use `impl IntoResponse` as the return type from handlers to avoid +//! typing large types. For example +//! +//! ```rust +//! use axum::http::StatusCode; +//! +//! async fn handler() -> (StatusCode, [(&'static str, &'static str); 1], &'static str) { +//! (StatusCode::OK, [("x-foo", "bar")], "Hello, World!") +//! } +//! ``` +//! +//! Becomes easier using `impl IntoResponse`: +//! +//! ```rust +//! use axum::{http::StatusCode, response::IntoResponse}; +//! +//! async fn impl_into_response() -> impl IntoResponse { +//! (StatusCode::OK, [("x-foo", "bar")], "Hello, World!") +//! } +//! ``` +//! +//! However `impl IntoResponse` has a few limitations. Firstly it can only be used +//! to return a single type: +//! +//! ```rust,compile_fail +//! use axum::{http::StatusCode, response::IntoResponse}; +//! +//! async fn handler() -> impl IntoResponse { +//! if check_something() { +//! StatusCode::NOT_FOUND +//! } else { +//! "Hello, World!" +//! } +//! } +//! +//! fn check_something() -> bool { +//! # false +//! // ... +//! } +//! ``` +//! +//! This function returns either a `StatusCode` or a `&'static str` which `impl +//! Trait` doesn't allow. +//! +//! Secondly `impl IntoResponse` can lead to type inference issues when used with +//! `Result` and `?`: +//! +//! ```rust,compile_fail +//! use axum::{http::StatusCode, response::IntoResponse}; +//! +//! async fn handler() -> impl IntoResponse { +//! create_thing()?; +//! Ok(StatusCode::CREATED) +//! } +//! +//! fn create_thing() -> Result<(), StatusCode> { +//! # Ok(()) +//! // ... +//! } +//! ``` +//! +//! This is because `?` supports using the [`From`] trait to convert to a different +//! error type but it doesn't know which type to convert to, because we only +//! specified `impl IntoResponse` as the return type. +//! +//! `Result` doesn't always work either: +//! +//! ```rust,compile_fail +//! use axum::{http::StatusCode, response::IntoResponse}; +//! +//! async fn handler() -> Result { +//! create_thing()?; +//! Ok(StatusCode::CREATED) +//! } +//! +//! fn create_thing() -> Result<(), StatusCode> { +//! # Ok(()) +//! // ... +//! } +//! ``` +//! +//! The solution is to use a concrete error type, such as `Result`: +//! +//! ```rust +//! use axum::{http::StatusCode, response::IntoResponse}; +//! +//! async fn handler() -> Result { +//! create_thing()?; +//! Ok(StatusCode::CREATED) +//! } +//! +//! fn create_thing() -> Result<(), StatusCode> { +//! # Ok(()) +//! // ... +//! } +//! ``` +//! +//! Because of this it is generally not recommended to use `impl IntoResponse` +//! unless you're familiar with the details of how `impl Trait` works. +//! +//! [`IntoResponse`]: crate::response::IntoResponse +//! [`IntoResponseParts`]: crate::response::IntoResponseParts +//! [`StatusCode`]: http::StatusCode use http::{header, HeaderValue, StatusCode}; diff --git a/axum/src/routing/method_routing.rs b/axum/src/routing/method_routing.rs index 1ab10db5..132cbbc8 100644 --- a/axum/src/routing/method_routing.rs +++ b/axum/src/routing/method_routing.rs @@ -951,7 +951,61 @@ where chained_service_fn!(put_service, PUT); chained_service_fn!(trace_service, TRACE); - #[doc = include_str!("../docs/method_routing/fallback.md")] + /// Add a fallback service to the router. + /// + /// This service will be called if no routes matches the incoming request. + /// + /// ```rust + /// use axum::{ + /// Router, + /// routing::get, + /// handler::Handler, + /// response::IntoResponse, + /// http::{StatusCode, Method, Uri}, + /// }; + /// + /// let handler = get(|| async {}).fallback(fallback); + /// + /// let app = Router::new().route("/", handler); + /// + /// async fn fallback(method: Method, uri: Uri) -> (StatusCode, String) { + /// (StatusCode::NOT_FOUND, format!("`{method}` not allowed for {uri}")) + /// } + /// # let _: Router = app; + /// ``` + /// + /// ## When used with `MethodRouter::merge` + /// + /// Two routers that both have a fallback cannot be merged. Doing so results in a + /// panic: + /// + /// ```rust,should_panic + /// use axum::{ + /// routing::{get, post}, + /// handler::Handler, + /// response::IntoResponse, + /// http::{StatusCode, Uri}, + /// }; + /// + /// let one = get(|| async {}).fallback(fallback_one); + /// + /// let two = post(|| async {}).fallback(fallback_two); + /// + /// let method_route = one.merge(two); + /// + /// async fn fallback_one() -> impl IntoResponse { /* ... */ } + /// async fn fallback_two() -> impl IntoResponse { /* ... */ } + /// # let app: axum::Router = axum::Router::new().route("/", method_route); + /// ``` + /// + /// ## Setting the `Allow` header + /// + /// By default `MethodRouter` will set the `Allow` header when returning `405 Method + /// Not Allowed`. This is also done when the fallback is used unless the response + /// generated by the fallback already sets the `Allow` header. + /// + /// This means if you use `fallback` to accept additional methods, you should make + /// sure you set the `Allow` header correctly. pub fn fallback_service(mut self, svc: T) -> Self where T: Service + Clone + Send + Sync + 'static, @@ -962,7 +1016,33 @@ where self } - #[doc = include_str!("../docs/method_routing/layer.md")] + /// Apply a [`tower::Layer`] to all routes in the router. + /// + /// This can be used to add additional processing to a request for a group + /// of routes. + /// + /// Note that the middleware is only applied to existing routes. So you have to + /// first add your routes (and / or fallback) and then call `layer` afterwards. Additional + /// routes added after `layer` is called will not have the middleware added. + /// + /// Works similarly to [`Router::layer`](super::Router::layer). See that method for + /// more details. + /// + /// # Example + /// + /// ```rust + /// use axum::{routing::get, Router}; + /// use tower::limit::ConcurrencyLimitLayer; + /// + /// async fn handler() {} + /// + /// let app = Router::new().route( + /// "/", + /// // All requests to `GET /` will be sent through `ConcurrencyLimitLayer` + /// get(handler).layer(ConcurrencyLimitLayer::new(64)), + /// ); + /// # let _: Router = app; + /// ``` pub fn layer(self, layer: L) -> MethodRouter where L: Layer> + Clone + Send + Sync + 'static, @@ -991,7 +1071,39 @@ where } } - #[doc = include_str!("../docs/method_routing/route_layer.md")] + /// Apply a [`tower::Layer`] to the router that will only run if the request matches + /// a route. + /// + /// Note that the middleware is only applied to existing routes. So you have to + /// first add your routes (and / or fallback) and then call `route_layer` + /// afterwards. Additional routes added after `route_layer` is called will not have + /// the middleware added. + /// + /// This works similarly to [`MethodRouter::layer`] except the middleware will only run if + /// the request matches a route. This is useful for middleware that return early + /// (such as authorization) which might otherwise convert a `405 Method Not Allowed` into a + /// `401 Unauthorized`. + /// + /// # Example + /// + /// ```rust + /// use axum::{ + /// routing::get, + /// Router, + /// }; + /// use tower_http::validate_request::ValidateRequestHeaderLayer; + /// + /// let app = Router::new().route( + /// "/foo", + /// get(|| async {}) + /// .route_layer(ValidateRequestHeaderLayer::bearer("password")) + /// ); + /// + /// // `GET /foo` with a valid token will receive `200 OK` + /// // `GET /foo` with a invalid token will receive `401 Unauthorized` + /// // `POST /FOO` with a invalid token will receive `405 Method Not Allowed` + /// # let _: Router = app; + /// ``` #[track_caller] pub fn route_layer(mut self, layer: L) -> Self where @@ -1085,7 +1197,29 @@ where Ok(self) } - #[doc = include_str!("../docs/method_routing/merge.md")] + /// Merge two routers into one. + /// + /// This is useful for breaking routers into smaller pieces and combining them + /// into one. + /// + /// ```rust + /// use axum::{ + /// routing::{get, post}, + /// Router, + /// }; + /// + /// let get = get(|| async {}); + /// let post = post(|| async {}); + /// + /// let merged = get.merge(post); + /// + /// let app = Router::new().route("/", merged); + /// + /// // Our app now accepts + /// // - GET / + /// // - POST / + /// # let _: Router = app; + /// ``` #[track_caller] pub fn merge(self, other: Self) -> Self { match self.merge_for_path(None, other) { diff --git a/axum/src/routing/mod.rs b/axum/src/routing/mod.rs index 58e60bd6..3b00a7d6 100644 --- a/axum/src/routing/mod.rs +++ b/axum/src/routing/mod.rs @@ -165,14 +165,211 @@ where } } - #[doc = include_str!("../docs/routing/without_v07_checks.md")] + /// Turn off checks for compatibility with route matching syntax from 0.7. + /// + /// This allows usage of paths starting with a colon `:` or an asterisk `*` which are otherwise prohibited. + /// + /// # Example + /// + /// ```rust + /// use axum::{ + /// routing::get, + /// Router, + /// }; + /// + /// let app = Router::<()>::new() + /// .without_v07_checks() + /// .route("/:colon", get(|| async {})) + /// .route("/*asterisk", get(|| async {})); + /// + /// // Our app now accepts + /// // - GET /:colon + /// // - GET /*asterisk + /// # let _: Router = app; + /// ``` + /// + /// Adding such routes without calling this method first will panic. + /// + /// ```rust,should_panic + /// use axum::{ + /// routing::get, + /// Router, + /// }; + /// + /// // This panics... + /// let app = Router::<()>::new() + /// .route("/:colon", get(|| async {})); + /// ``` + /// + /// # Merging + /// + /// When two routers are merged, v0.7 checks are disabled for route registrations on the resulting router if both of the two routers had them also disabled. + /// + /// # Nesting + /// + /// Each router needs to have the checks explicitly disabled. Nesting a router with the checks either enabled or disabled has no effect on the outer router. pub fn without_v07_checks(self) -> Self { tap_inner!(self, mut this => { this.path_router.without_v07_checks(); }) } - #[doc = include_str!("../docs/routing/route.md")] + /// Add another route to the router. + /// + /// `path` is a string of path segments separated by `/`. Each segment + /// can be either static, a capture, or a wildcard. + /// + /// `method_router` is the [`MethodRouter`] that should receive the request if the + /// path matches `path`. Usually, `method_router` will be a handler wrapped in a method + /// router like [`get`]. See [`handler`](crate::handler) for more details on handlers. + /// + /// # Static paths + /// + /// Examples: + /// + /// - `/` + /// - `/foo` + /// - `/users/123` + /// + /// If the incoming request matches the path exactly the corresponding service will + /// be called. + /// + /// # Captures + /// + /// Paths can contain segments like `/{key}` which matches any single segment and + /// will store the value captured at `key`. The value captured can be zero-length + /// except for in the invalid path `//`. + /// + /// Examples: + /// + /// - `/{key}` + /// - `/users/{id}` + /// - `/users/{id}/tweets` + /// + /// Captures can be extracted using [`Path`](crate::extract::Path). See its + /// documentation for more details. + /// + /// It is not possible to create segments that only match some types like numbers or + /// regular expression. You must handle that manually in your handlers. + /// + /// [`MatchedPath`] can be used to extract the matched path rather than the actual path. + /// + /// # Wildcards + /// + /// Paths can end in `/{*key}` which matches all segments and will store the segments + /// captured at `key`. + /// + /// Examples: + /// + /// - `/{*key}` + /// - `/assets/{*path}` + /// - `/{id}/{repo}/{*tree}` + /// + /// Note that `/{*key}` doesn't match empty segments. Thus: + /// + /// - `/{*key}` doesn't match `/` but does match `/a`, `/a/`, etc. + /// - `/x/{*key}` doesn't match `/x` or `/x/` but does match `/x/a`, `/x/a/`, etc. + /// + /// Wildcard captures can also be extracted using [`Path`](crate::extract::Path): + /// + /// ```rust + /// use axum::{ + /// Router, + /// routing::get, + /// extract::Path, + /// }; + /// + /// let app: Router = Router::new().route("/{*key}", get(handler)); + /// + /// async fn handler(Path(path): Path) -> String { + /// path + /// } + /// ``` + /// + /// Note that the leading slash is not included, i.e. for the route `/foo/{*rest}` and + /// the path `/foo/bar/baz` the value of `rest` will be `bar/baz`. + /// + /// # Accepting multiple methods + /// + /// To accept multiple methods for the same route you can add all handlers at the + /// same time: + /// + /// ```rust + /// use axum::{Router, routing::{get, delete}, extract::Path}; + /// + /// let app = Router::new().route( + /// "/", + /// get(get_root).post(post_root).delete(delete_root), + /// ); + /// + /// async fn get_root() {} + /// + /// async fn post_root() {} + /// + /// async fn delete_root() {} + /// # let _: Router = app; + /// ``` + /// + /// Or you can add them one by one: + /// + /// ```rust + /// # use axum::Router; + /// # use axum::routing::{get, post, delete}; + /// # + /// let app = Router::new() + /// .route("/", get(get_root)) + /// .route("/", post(post_root)) + /// .route("/", delete(delete_root)); + /// # + /// # let _: Router = app; + /// # async fn get_root() {} + /// # async fn post_root() {} + /// # async fn delete_root() {} + /// ``` + /// + /// # More examples + /// + /// ```rust + /// use axum::{Router, routing::{get, delete}, extract::Path}; + /// + /// 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_users_action)) + /// .route("/assets/{*path}", get(serve_asset)); + /// + /// async fn root() {} + /// + /// async fn list_users() {} + /// + /// async fn create_user() {} + /// + /// async fn show_user(Path(id): Path) {} + /// + /// async fn do_users_action(Path((version, id)): Path<(String, u64)>) {} + /// + /// async fn serve_asset(Path(path): Path) {} + /// # let _: Router = app; + /// ``` + /// + /// # Panics + /// + /// Panics if the route overlaps with another route: + /// + /// ```rust,should_panic + /// use axum::{routing::get, Router}; + /// + /// let app = Router::new() + /// .route("/", get(|| async {})) + /// .route("/", get(|| async {})); + /// # let _: Router = app; + /// ``` + /// + /// The static route `/foo` and the dynamic route `/{key}` are not considered to + /// overlap and `/foo` will take precedence. + /// + /// Also panics if `path` is empty. #[track_caller] pub fn route(self, path: &str, method_router: MethodRouter) -> Self { tap_inner!(self, mut this => { @@ -180,7 +377,76 @@ where }) } - #[doc = include_str!("../docs/routing/route_service.md")] + /// Add another route to the router that calls a [`Service`]. + /// + /// # Example + /// + /// ```rust,no_run + /// use axum::{ + /// Router, + /// body::Body, + /// routing::{any_service, get_service}, + /// extract::Request, + /// http::StatusCode, + /// error_handling::HandleErrorLayer, + /// }; + /// use tower_http::services::ServeFile; + /// use http::Response; + /// use std::{convert::Infallible, io}; + /// use tower::service_fn; + /// + /// let app = Router::new() + /// .route( + /// // Any request to `/` goes to a service + /// "/", + /// // Services whose response body is not `axum::body::BoxBody` + /// // can be wrapped in `axum::routing::any_service` (or one of the other routing filters) + /// // to have the response body mapped + /// any_service(service_fn(|_: Request| async { + /// let res = Response::new(Body::from("Hi from `GET /`")); + /// Ok::<_, Infallible>(res) + /// })) + /// ) + /// .route_service( + /// "/foo", + /// // This service's response body is `axum::body::BoxBody` so + /// // it can be routed to directly. + /// service_fn(|req: Request| async move { + /// let body = Body::from(format!("Hi from `{} /foo`", req.method())); + /// let res = Response::new(body); + /// Ok::<_, Infallible>(res) + /// }) + /// ) + /// .route_service( + /// // GET `/static/Cargo.toml` goes to a service from tower-http + /// "/static/Cargo.toml", + /// ServeFile::new("Cargo.toml"), + /// ); + /// # let _: Router = app; + /// ``` + /// + /// Routing to arbitrary services in this way has complications for backpressure + /// ([`Service::poll_ready`]). See the [Routing to services and backpressure] module + /// for more details. + /// + /// # Panics + /// + /// Panics for the same reasons as [`Router::route`] or if you attempt to route to a + /// `Router`: + /// + /// ```rust,should_panic + /// use axum::{routing::get, Router}; + /// + /// let app = Router::new().route_service( + /// "/", + /// Router::new().route("/foo", get(|| async {})), + /// ); + /// # let _: Router = app; + /// ``` + /// + /// Use [`Router::nest`] instead. + /// + /// [Routing to services and backpressure]: middleware/index.html#routing-to-servicesmiddleware-and-backpressure pub fn route_service(self, path: &str, service: T) -> Self where T: Service + Clone + Send + Sync + 'static, @@ -199,7 +465,200 @@ where }) } - #[doc = include_str!("../docs/routing/nest.md")] + /// Nest a [`Router`] at some path. + /// + /// This allows you to break your application into smaller pieces and compose + /// them together. + /// + /// # Example + /// + /// ```rust + /// use axum::{ + /// routing::{get, post}, + /// Router, + /// }; + /// + /// let user_routes = Router::new().route("/{id}", get(|| async {})); + /// + /// let team_routes = Router::new().route("/", post(|| async {})); + /// + /// let api_routes = Router::new() + /// .nest("/users", user_routes) + /// .nest("/teams", team_routes); + /// + /// let app = Router::new().nest("/api", api_routes); + /// + /// // Our app now accepts + /// // - GET /api/users/{id} + /// // - POST /api/teams + /// # let _: Router = app; + /// ``` + /// + /// # How the URI changes + /// + /// Note that nested routes will not see the original request URI but instead + /// have the matched prefix stripped. This is necessary for services like static + /// file serving to work. Use [`OriginalUri`] if you need the original request + /// URI. + /// + /// # Captures from outer routes + /// + /// Take care when using `nest` together with dynamic routes as nesting also + /// captures from the outer routes: + /// + /// ```rust + /// use axum::{ + /// extract::Path, + /// routing::get, + /// Router, + /// }; + /// use std::collections::HashMap; + /// + /// async fn users_get(Path(params): Path>) { + /// // 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); + /// # let _: Router = app; + /// ``` + /// + /// # Differences from wildcard routes + /// + /// Nested routes are similar to wildcard routes. The difference is that + /// wildcard routes still see the whole URI whereas nested routes will have + /// the prefix stripped: + /// + /// ```rust + /// use axum::{routing::get, http::Uri, Router}; + /// + /// let nested_router = Router::new() + /// .route("/", get(|uri: Uri| async { + /// // `uri` will _not_ contain `/bar` + /// })); + /// + /// let app = Router::new() + /// .route("/foo/{*rest}", get(|uri: Uri| async { + /// // `uri` will contain `/foo` + /// })) + /// .nest("/bar", nested_router); + /// # let _: Router = app; + /// ``` + /// + /// Additionally, while the wildcard route `/foo/*rest` will not match the + /// paths `/foo` or `/foo/`, a nested router at `/foo` will match the path `/foo` + /// (but not `/foo/`), and a nested router at `/foo/` will match the path `/foo/` + /// (but not `/foo`). + /// + /// # Fallbacks + /// + /// If a nested router doesn't have its own fallback then it will inherit the + /// fallback from the outer router: + /// + /// ```rust + /// use axum::{routing::get, http::StatusCode, handler::Handler, Router}; + /// + /// async fn fallback() -> (StatusCode, &'static str) { + /// (StatusCode::NOT_FOUND, "Not Found") + /// } + /// + /// let api_routes = Router::new().route("/users", get(|| async {})); + /// + /// let app = Router::new() + /// .nest("/api", api_routes) + /// .fallback(fallback); + /// # let _: Router = app; + /// ``` + /// + /// Here requests like `GET /api/not-found` will go into `api_routes` but because + /// it doesn't have a matching route and doesn't have its own fallback it will call + /// the fallback from the outer router, i.e. the `fallback` function. + /// + /// If the nested router has its own fallback then the outer fallback will not be + /// inherited: + /// + /// ```rust + /// use axum::{ + /// routing::get, + /// http::StatusCode, + /// handler::Handler, + /// Json, + /// Router, + /// }; + /// + /// async fn fallback() -> (StatusCode, &'static str) { + /// (StatusCode::NOT_FOUND, "Not Found") + /// } + /// + /// async fn api_fallback() -> (StatusCode, Json) { + /// ( + /// StatusCode::NOT_FOUND, + /// Json(serde_json::json!({ "status": "Not Found" })), + /// ) + /// } + /// + /// let api_routes = Router::new() + /// .route("/users", get(|| async {})) + /// .fallback(api_fallback); + /// + /// let app = Router::new() + /// .nest("/api", api_routes) + /// .fallback(fallback); + /// # let _: Router = app; + /// ``` + /// + /// Here requests like `GET /api/not-found` will go to `api_fallback`. + /// + /// # Nesting routers with state + /// + /// When combining [`Router`]s with this method, each [`Router`] must have the + /// same type of state. If your routers have different types you can use + /// [`Router::with_state`] to provide the state and make the types match: + /// + /// ```rust + /// use axum::{ + /// Router, + /// routing::get, + /// extract::State, + /// }; + /// + /// #[derive(Clone)] + /// struct InnerState {} + /// + /// #[derive(Clone)] + /// struct OuterState {} + /// + /// async fn inner_handler(state: State) {} + /// + /// let inner_router = Router::new() + /// .route("/bar", get(inner_handler)) + /// .with_state(InnerState {}); + /// + /// async fn outer_handler(state: State) {} + /// + /// let app = Router::new() + /// .route("/", get(outer_handler)) + /// .nest("/foo", inner_router) + /// .with_state(OuterState {}); + /// # let _: axum::Router = app; + /// ``` + /// + /// Note that the inner router will still inherit the fallback from the outer + /// router. + /// + /// # Panics + /// + /// - If the route overlaps with another route. See [`Router::route`] + /// for more details. + /// - If the route contains a wildcard (`*`). + /// - If `path` is empty. + /// + /// [`OriginalUri`]: crate::extract::OriginalUri + /// [fallbacks]: Router::fallback #[doc(alias = "scope")] // Some web frameworks like actix-web use this term #[track_caller] pub fn nest(self, path: &str, router: Self) -> Self { @@ -238,7 +697,86 @@ where }) } - #[doc = include_str!("../docs/routing/merge.md")] + /// Merge the paths and fallbacks of two routers into a single [`Router`]. + /// + /// This is useful for breaking apps into smaller pieces and combining them + /// into one. + /// + /// ```rust + /// use axum::{ + /// routing::get, + /// Router, + /// }; + /// # + /// # async fn users_list() {} + /// # async fn users_show() {} + /// # async fn teams_list() {} + /// + /// // define some routes separately + /// let user_routes = Router::new() + /// .route("/users", get(users_list)) + /// .route("/users/{id}", get(users_show)); + /// + /// let team_routes = Router::new() + /// .route("/teams", get(teams_list)); + /// + /// // combine them into one + /// let app = Router::new() + /// .merge(user_routes) + /// .merge(team_routes); + /// + /// // could also do `user_routes.merge(team_routes)` + /// + /// // Our app now accepts + /// // - GET /users + /// // - GET /users/{id} + /// // - GET /teams + /// # let _: Router = app; + /// ``` + /// + /// # Merging routers with state + /// + /// When combining [`Router`]s with this method, each [`Router`] must have the + /// same type of state. If your routers have different types you can use + /// [`Router::with_state`] to provide the state and make the types match: + /// + /// ```rust + /// use axum::{ + /// Router, + /// routing::get, + /// extract::State, + /// }; + /// + /// #[derive(Clone)] + /// struct InnerState {} + /// + /// #[derive(Clone)] + /// struct OuterState {} + /// + /// async fn inner_handler(state: State) {} + /// + /// let inner_router = Router::new() + /// .route("/bar", get(inner_handler)) + /// .with_state(InnerState {}); + /// + /// async fn outer_handler(state: State) {} + /// + /// let app = Router::new() + /// .route("/", get(outer_handler)) + /// .merge(inner_router) + /// .with_state(OuterState {}); + /// # let _: axum::Router = app; + /// ``` + /// + /// # Merging routers with fallbacks + /// + /// When combining [`Router`]s with this method, the [fallback](Router::fallback) is also merged. + /// However only one of the routers can have a fallback. + /// + /// # Panics + /// + /// - If two routers that each have a [fallback](Router::fallback) are merged. This + /// is because `Router` only allows a single fallback. #[track_caller] pub fn merge(self, other: R) -> Self where @@ -277,7 +815,73 @@ where }) } - #[doc = include_str!("../docs/routing/layer.md")] + /// Apply a [`tower::Layer`] to all routes in the router. + /// + /// This can be used to add additional processing to a request for a group + /// of routes. + /// + /// Note that the middleware is only applied to existing routes. So you have to + /// first add your routes (and / or fallback) and then call `layer` afterwards. Additional + /// routes added after `layer` is called will not have the middleware added. + /// + /// If you want to add middleware to a single handler you can either use + /// [`MethodRouter::layer`] or [`Handler::layer`]. + /// + /// # Example + /// + /// Adding the [`tower_http::trace::TraceLayer`]: + /// + /// ```rust + /// use axum::{routing::get, Router}; + /// use tower_http::trace::TraceLayer; + /// + /// let app = Router::new() + /// .route("/foo", get(|| async {})) + /// .route("/bar", get(|| async {})) + /// .layer(TraceLayer::new_for_http()); + /// # let _: Router = app; + /// ``` + /// + /// If you need to write your own middleware see ["Writing + /// middleware"](crate::middleware#writing-middleware) for the different options. + /// + /// If you only want middleware on some routes you can use [`Router::merge`]: + /// + /// ```rust + /// use axum::{routing::get, Router}; + /// use tower_http::{trace::TraceLayer, compression::CompressionLayer}; + /// + /// let with_tracing = Router::new() + /// .route("/foo", get(|| async {})) + /// .layer(TraceLayer::new_for_http()); + /// + /// let with_compression = Router::new() + /// .route("/bar", get(|| async {})) + /// .layer(CompressionLayer::new()); + /// + /// // Merge everything into one `Router` + /// let app = Router::new() + /// .merge(with_tracing) + /// .merge(with_compression); + /// # let _: Router = app; + /// ``` + /// + /// # Multiple middleware + /// + /// It's recommended to use [`tower::ServiceBuilder`] when applying multiple + /// middleware. See [`middleware`](crate::middleware) for more details. + /// + /// # Runs after routing + /// + /// Middleware added with this method will run _after_ routing and thus cannot be + /// used to rewrite the request URI. See ["Rewriting request URI in + /// middleware"](crate::middleware#rewriting-request-uri-in-middleware) for more + /// details and a workaround. + /// + /// # Error handling + /// + /// See [`middleware`](crate::middleware) for details on how error handling impacts + /// middleware. pub fn layer(self, layer: L) -> Self where L: Layer + Clone + Send + Sync + 'static, @@ -293,7 +897,41 @@ where }) } - #[doc = include_str!("../docs/routing/route_layer.md")] + /// Apply a [`tower::Layer`] to the router that will only run if the request matches + /// a route. + /// + /// Note that the middleware is only applied to existing routes. So you have to + /// first add your routes (and / or fallback) and then call `route_layer` + /// afterwards. Additional routes added after `route_layer` is called will not have + /// the middleware added. + /// + /// This works similarly to [`Router::layer`] except the middleware will only run if + /// the request matches a route. This is useful for middleware that return early + /// (such as authorization) which might otherwise convert a `404 Not Found` into a + /// `401 Unauthorized`. + /// + /// This function will panic if no routes have been declared yet on the router, + /// since the new layer will have no effect, and this is typically a bug. + /// In generic code, you can test if that is the case first, by calling [`Router::has_routes`]. + /// + /// # Example + /// + /// ```rust + /// use axum::{ + /// routing::get, + /// Router, + /// }; + /// use tower_http::validate_request::ValidateRequestHeaderLayer; + /// + /// let app = Router::new() + /// .route("/foo", get(|| async {})) + /// .route_layer(ValidateRequestHeaderLayer::bearer("password")); + /// + /// // `GET /foo` with a valid token will receive `200 OK` + /// // `GET /foo` with a invalid token will receive `401 Unauthorized` + /// // `GET /not-found` with a invalid token will receive `404 Not Found` + /// # let _: Router = app; + /// ``` #[track_caller] pub fn route_layer(self, layer: L) -> Self where @@ -316,8 +954,68 @@ where self.inner.path_router.has_routes() } + /// Add a fallback [`Handler`] to the router. + /// + /// This service will be called if no routes matches the incoming request. + /// + /// ```rust + /// use axum::{ + /// Router, + /// routing::get, + /// handler::Handler, + /// response::IntoResponse, + /// http::{StatusCode, Uri}, + /// }; + /// + /// let app = Router::new() + /// .route("/foo", get(|| async { /* ... */ })) + /// .fallback(fallback); + /// + /// async fn fallback(uri: Uri) -> (StatusCode, String) { + /// (StatusCode::NOT_FOUND, format!("No route for {uri}")) + /// } + /// # let _: Router = app; + /// ``` + /// + /// Fallbacks only apply to routes that aren't matched by anything in the + /// router. If a handler is matched by a request but returns 404 the + /// fallback is not called. Note that this applies to [`MethodRouter`]s too: if the + /// request hits a valid path but the [`MethodRouter`] does not have an appropriate + /// method handler installed, the fallback is not called (use + /// [`MethodRouter::fallback`] for this purpose instead). + /// + /// + /// # Handling all requests without other routes + /// + /// Using `Router::new().fallback(...)` to accept all request regardless of path or + /// method, if you don't have other routes, isn't optimal: + /// + /// ```rust + /// use axum::Router; + /// + /// async fn handler() {} + /// + /// let app = Router::new().fallback(handler); + /// + /// # async { + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// axum::serve(listener, app).await.unwrap(); + /// # }; + /// ``` + /// + /// Running the handler directly is faster since it avoids the overhead of routing: + /// + /// ```rust + /// use axum::handler::HandlerWithoutStateExt; + /// + /// async fn handler() {} + /// + /// # async { + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// axum::serve(listener, handler.into_make_service()).await.unwrap(); + /// # }; + /// ``` #[track_caller] - #[doc = include_str!("../docs/routing/fallback.md")] pub fn fallback(self, handler: H) -> Self where H: Handler, @@ -346,7 +1044,44 @@ where .fallback_endpoint(Endpoint::Route(route)) } - #[doc = include_str!("../docs/routing/method_not_allowed_fallback.md")] + /// Add a fallback [`Handler`] for the case where a route exists, but the method of the request is not supported. + /// + /// Sets a fallback on all previously registered [`MethodRouter`]s, + /// to be called when no matching method handler is set. + /// + /// ```rust,no_run + /// use axum::{response::IntoResponse, routing::get, Router}; + /// + /// async fn hello_world() -> impl IntoResponse { + /// "Hello, world!\n" + /// } + /// + /// async fn default_fallback() -> impl IntoResponse { + /// "Default fallback\n" + /// } + /// + /// async fn handle_405() -> impl IntoResponse { + /// "Method not allowed fallback" + /// } + /// + /// #[tokio::main] + /// async fn main() { + /// let router = Router::new() + /// .route("/", get(hello_world)) + /// .fallback(default_fallback) + /// .method_not_allowed_fallback(handle_405); + /// + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// + /// axum::serve(listener, router).await.unwrap(); + /// } + /// ``` + /// + /// The fallback only applies if there is a `MethodRouter` registered for a given path, + /// but the method used in the request is not specified. In the example, a `GET` on + /// `http://localhost:3000` causes the `hello_world` handler to react, while issuing a + /// `POST` triggers `handle_405`. Calling an entirely different route, like `http://localhost:3000/hello` + /// causes `default_fallback` to run. #[allow(clippy::needless_pass_by_value)] pub fn method_not_allowed_fallback(self, handler: H) -> Self where @@ -423,7 +1158,237 @@ where }) } - #[doc = include_str!("../docs/routing/with_state.md")] + /// Provide the state for the router. State passed to this method is global and will be used + /// for all requests this router receives. That means it is not suitable for holding state derived from a request, such as authorization data extracted in a middleware. Use [`Extension`] instead for such data. + /// + /// ```rust + /// use axum::{Router, routing::get, extract::State}; + /// + /// #[derive(Clone)] + /// struct AppState {} + /// + /// let routes = Router::new() + /// .route("/", get(|State(state): State| async { + /// // use state + /// })) + /// .with_state(AppState {}); + /// + /// # async { + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// axum::serve(listener, routes).await.unwrap(); + /// # }; + /// ``` + /// + /// # Returning routers with states from functions + /// + /// When returning `Router`s from functions, it is generally recommended not to set the + /// state directly: + /// + /// ```rust + /// use axum::{Router, routing::get, extract::State}; + /// + /// #[derive(Clone)] + /// struct AppState {} + /// + /// // Don't call `Router::with_state` here + /// fn routes() -> Router { + /// Router::new() + /// .route("/", get(|_: State| async {})) + /// } + /// + /// // Instead do it before you run the server + /// let routes = routes().with_state(AppState {}); + /// + /// # async { + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// axum::serve(listener, routes).await.unwrap(); + /// # }; + /// ``` + /// + /// If you do need to provide the state, and you're _not_ nesting/merging the router + /// into another router, then return `Router` without any type parameters: + /// + /// ```rust + /// # use axum::{Router, routing::get, extract::State}; + /// # #[derive(Clone)] + /// # struct AppState {} + /// # + /// // Don't return `Router` + /// fn routes(state: AppState) -> Router { + /// Router::new() + /// .route("/", get(|_: State| async {})) + /// .with_state(state) + /// } + /// + /// let routes = routes(AppState {}); + /// + /// # async { + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// axum::serve(listener, routes).await.unwrap(); + /// # }; + /// ``` + /// + /// This is because we can only call `Router::into_make_service` on `Router<()>`, + /// not `Router`. See below for more details about why that is. + /// + /// Note that the state defaults to `()` so `Router` and `Router<()>` is the same. + /// + /// If you are nesting/merging the router it is recommended to use a generic state + /// type on the resulting router: + /// + /// ```rust + /// # use axum::{Router, routing::get, extract::State}; + /// # #[derive(Clone)] + /// # struct AppState {} + /// # + /// fn routes(state: AppState) -> Router { + /// Router::new() + /// .route("/", get(|_: State| async {})) + /// .with_state(state) + /// } + /// + /// let routes = Router::new().nest("/api", routes(AppState {})); + /// + /// # async { + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// axum::serve(listener, routes).await.unwrap(); + /// # }; + /// ``` + /// + /// # What `S` in `Router` means + /// + /// `Router` means a router that is _missing_ a state of type `S` to be able to + /// handle requests. It does _not_ mean a `Router` that _has_ a state of type `S`. + /// + /// For example: + /// + /// ```rust + /// # use axum::{Router, routing::get, extract::State}; + /// # #[derive(Clone)] + /// # struct AppState {} + /// # + /// // A router that _needs_ an `AppState` to handle requests + /// let router: Router = Router::new() + /// .route("/", get(|_: State| async {})); + /// + /// // Once we call `Router::with_state` the router isn't missing + /// // the state anymore, because we just provided it + /// // + /// // Therefore the router type becomes `Router<()>`, i.e a router + /// // that is not missing any state + /// let router: Router<()> = router.with_state(AppState {}); + /// + /// // Only `Router<()>` has the `into_make_service` method. + /// // + /// // You cannot call `into_make_service` on a `Router` + /// // because it is still missing an `AppState`. + /// # async { + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// axum::serve(listener, router).await.unwrap(); + /// # }; + /// ``` + /// + /// Perhaps a little counter intuitively, `Router::with_state` doesn't always return a + /// `Router<()>`. Instead you get to pick what the new missing state type is: + /// + /// ```rust + /// # use axum::{Router, routing::get, extract::State}; + /// # #[derive(Clone)] + /// # struct AppState {} + /// # + /// let router: Router = Router::new() + /// .route("/", get(|_: State| async {})); + /// + /// // When we call `with_state` we're able to pick what the next missing state type is. + /// // Here we pick `String`. + /// let string_router: Router = router.with_state(AppState {}); + /// + /// // That allows us to add new routes that uses `String` as the state type + /// let string_router = string_router + /// .route("/needs-string", get(|_: State| async {})); + /// + /// // Provide the `String` and choose `()` as the new missing state. + /// let final_router: Router<()> = string_router.with_state("foo".to_owned()); + /// + /// // Since we have a `Router<()>` we can run it. + /// # async { + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// axum::serve(listener, final_router).await.unwrap(); + /// # }; + /// ``` + /// + /// This why this returning `Router` after calling `with_state` doesn't + /// work: + /// + /// ```rust,compile_fail + /// # use axum::{Router, routing::get, extract::State}; + /// # #[derive(Clone)] + /// # struct AppState {} + /// # + /// // This won't work because we're returning a `Router` + /// // i.e. we're saying we're still missing an `AppState` + /// fn routes(state: AppState) -> Router { + /// Router::new() + /// .route("/", get(|_: State| async {})) + /// .with_state(state) + /// } + /// + /// let app = routes(AppState {}); + /// + /// // We can only call `Router::into_make_service` on a `Router<()>` + /// // but `app` is a `Router` + /// # async { + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// axum::serve(listener, app).await.unwrap(); + /// # }; + /// ``` + /// + /// Instead return `Router<()>` since we have provided all the state needed: + /// + /// ```rust + /// # use axum::{Router, routing::get, extract::State}; + /// # #[derive(Clone)] + /// # struct AppState {} + /// # + /// // We've provided all the state necessary so return `Router<()>` + /// fn routes(state: AppState) -> Router<()> { + /// Router::new() + /// .route("/", get(|_: State| async {})) + /// .with_state(state) + /// } + /// + /// let app = routes(AppState {}); + /// + /// // We can now call `Router::into_make_service` + /// # async { + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// axum::serve(listener, app).await.unwrap(); + /// # }; + /// ``` + /// + /// # A note about performance + /// + /// If you need a `Router` that implements `Service` but you don't need any state (perhaps + /// you're making a library that uses axum internally) then it is recommended to call this + /// method before you start serving requests: + /// + /// ```rust + /// use axum::{Router, routing::get}; + /// + /// let app = Router::new() + /// .route("/", get(|| async { /* ... */ })) + /// // even though we don't need any state, call `with_state(())` anyway + /// .with_state(()); + /// # let _: Router = app; + /// ``` + /// + /// This is not required but it gives axum a chance to update some internals in the router + /// which may impact performance and reduce allocations. + /// + /// Note that [`Router::into_make_service`] and [`Router::into_make_service_with_connect_info`] + /// do this automatically. + /// + /// [`Extension`]: crate::Extension pub fn with_state(self, state: S) -> Router { map_inner!(self, this => RouterInner { path_router: this.path_router.with_state(state.clone()), @@ -544,7 +1509,79 @@ impl Router { IntoMakeService::new(self.with_state(())) } - #[doc = include_str!("../docs/routing/into_make_service_with_connect_info.md")] + /// Convert this router into a [`MakeService`], that will store `C`'s + /// associated `ConnectInfo` in a request extension such that [`ConnectInfo`] + /// can extract it. + /// + /// This enables extracting things like the client's remote address. + /// + /// Extracting [`std::net::SocketAddr`] is supported out of the box: + /// + /// ```rust + /// use axum::{ + /// extract::ConnectInfo, + /// routing::get, + /// Router, + /// }; + /// use std::net::SocketAddr; + /// + /// let app = Router::new().route("/", get(handler)); + /// + /// async fn handler(ConnectInfo(addr): ConnectInfo) -> String { + /// format!("Hello {addr}") + /// } + /// + /// # async { + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// axum::serve(listener, app.into_make_service_with_connect_info::()).await.unwrap(); + /// # }; + /// ``` + /// + /// You can implement custom a [`Connected`] like so: + /// + /// ```rust + /// use axum::{ + /// extract::connect_info::{ConnectInfo, Connected}, + /// routing::get, + /// serve::IncomingStream, + /// Router, + /// }; + /// use tokio::net::TcpListener; + /// + /// let app = Router::new().route("/", get(handler)); + /// + /// async fn handler( + /// ConnectInfo(my_connect_info): ConnectInfo, + /// ) -> String { + /// format!("Hello {my_connect_info:?}") + /// } + /// + /// #[derive(Clone, Debug)] + /// struct MyConnectInfo { + /// // ... + /// } + /// + /// impl Connected> for MyConnectInfo { + /// fn connect_info(target: IncomingStream<'_, TcpListener>) -> Self { + /// MyConnectInfo { + /// // ... + /// } + /// } + /// } + /// + /// # async { + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// axum::serve(listener, app.into_make_service_with_connect_info::()).await.unwrap(); + /// # }; + /// ``` + /// + /// See the [unix domain socket example][uds] for an example of how to use + /// this to collect UDS connection info. + /// + /// [`MakeService`]: tower::make::MakeService + /// [`Connected`]: crate::extract::connect_info::Connected + /// [`ConnectInfo`]: crate::extract::connect_info::ConnectInfo + /// [uds]: https://github.com/tokio-rs/axum/blob/main/examples/unix-domain-socket/src/main.rs #[cfg(feature = "tokio")] #[must_use] pub fn into_make_service_with_connect_info(self) -> IntoMakeServiceWithConnectInfo {