diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 97babe78..2c307695 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -69,7 +69,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - rust: [stable, beta, nightly, 1.51] + rust: [stable, beta, nightly, 1.54] steps: - uses: actions/checkout@master - uses: actions-rs/toolchain@v1 diff --git a/README.md b/README.md index 0973f825..8a53a1c5 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ This crate uses `#![forbid(unsafe_code)]` to ensure everything is implemented in ## Minimum supported Rust version -axum's MSRV is 1.51. +axum 0.2's MSRV is 1.51. axum 0.3's (still work-in-progress) MSRV will be 1.54. ## Examples diff --git a/src/docs/applying_middleware.md b/src/docs/applying_middleware.md new file mode 100644 index 00000000..42246dee --- /dev/null +++ b/src/docs/applying_middleware.md @@ -0,0 +1,253 @@ +# Applying middleware + +axum is designed to take full advantage of the tower and tower-http +ecosystem of middleware. + +If you're new to tower we recommend you read its [guides][tower-guides] for +a general introduction to tower and its concepts. + +## To individual handlers + +A middleware can be applied to a single handler like so: + +```rust,no_run +use axum::{ + handler::Handler, + routing::get, + Router, +}; +use tower::limit::ConcurrencyLimitLayer; + +let app = Router::new() + .route( + "/", + get(handler.layer(ConcurrencyLimitLayer::new(100))), + ); + +async fn handler() {} +# async { +# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); +# }; +``` + +## To groups of routes + +Middleware can also be applied to a group of routes like so: + +```rust,no_run +use axum::{ + routing::{get, post}, + Router, +}; +use tower::limit::ConcurrencyLimitLayer; + +async fn handler() {} + +let app = Router::new() + .route("/", get(handler)) + .route("/foo", post(handler)) + .layer(ConcurrencyLimitLayer::new(100)); +# async { +# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); +# }; +``` + +Note that [`Router::layer`] applies the middleware to all previously added +routes, of that particular `Router`. If you need multiple groups of routes +with different middleware build them separately and combine them with +[`Router::merge`]: + +```rust,no_run +use axum::{ + routing::{get, post}, + Router, +}; +use tower::limit::ConcurrencyLimitLayer; +# type MyAuthLayer = tower::layer::util::Identity; + +async fn handler() {} + +let foo = Router::new() + .route("/", get(handler)) + .route("/foo", post(handler)) + .layer(ConcurrencyLimitLayer::new(100)); + +let bar = Router::new() + .route("/requires-auth", get(handler)) + .layer(MyAuthLayer::new()); + +let app = foo.merge(bar); +# async { +# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); +# }; +``` + +## Applying multiple middleware + +[`tower::ServiceBuilder`] can be used to combine multiple middleware: + +```rust,no_run +use axum::{ + body::Body, + routing::get, + http::{Request, StatusCode}, + error_handling::HandleErrorLayer, + response::IntoResponse, + Router, BoxError, +}; +use tower::ServiceBuilder; +use tower_http::compression::CompressionLayer; +use std::{borrow::Cow, time::Duration}; + +let middleware_stack = ServiceBuilder::new() + // Handle errors from middleware + // + // This middleware most be added above any fallible + // ones if you're using `ServiceBuilder`, due to how ordering works + .layer(HandleErrorLayer::new(handle_error)) + // Return an error after 30 seconds + .timeout(Duration::from_secs(30)) + // Shed load if we're receiving too many requests + .load_shed() + // Process at most 100 requests concurrently + .concurrency_limit(100) + // Compress response bodies + .layer(CompressionLayer::new()); + +let app = Router::new() + .route("/", get(|_: Request| async { /* ... */ })) + .layer(middleware_stack); + +fn handle_error(error: BoxError) -> impl IntoResponse { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Something went wrong: {}", error), + ) +} +# async { +# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); +# }; +``` + +See [Error handling](#error-handling) for more details on general error handling in axum. + +## Commonly used middleware + +[`tower::util`] and [`tower_http`] have a large collection of middleware that are compatible +with axum. Some commonly used are: + +```rust,no_run +use axum::{ + body::{Body, BoxBody}, + routing::get, + http::{Request, Response}, + error_handling::HandleErrorLayer, + Router, +}; +use tower::{ + filter::AsyncFilterLayer, + util::AndThenLayer, + ServiceBuilder, +}; +use std::convert::Infallible; +use tower_http::trace::TraceLayer; +# +# fn handle_error(error: T) -> axum::http::StatusCode { +# axum::http::StatusCode::INTERNAL_SERVER_ERROR +# } + +let middleware_stack = ServiceBuilder::new() + // Handle errors from middleware + // + // This middleware most be added above any fallible + // ones if you're using `ServiceBuilder`, due to how ordering works + .layer(HandleErrorLayer::new(handle_error)) + // `TraceLayer` adds high level tracing and logging + .layer(TraceLayer::new_for_http()) + // `AsyncFilterLayer` lets you asynchronously transform the request + .layer(AsyncFilterLayer::new(map_request)) + // `AndThenLayer` lets you asynchronously transform the response + .layer(AndThenLayer::new(map_response)); + +async fn map_request(req: Request) -> Result, Infallible> { + Ok(req) +} + +async fn map_response(res: Response) -> Result, Infallible> { + Ok(res) +} + +let app = Router::new() + .route("/", get(|| async { /* ... */ })) + .layer(middleware_stack); +# async { +# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); +# }; +``` + +Additionally axum provides [`extract::extractor_middleware()`] for converting any extractor into +a middleware. Among other things, this can be useful for doing authorization. See +[`extract::extractor_middleware()`] for more details. + +See [Error handling](#error-handling) for more details on general error handling in axum. + +## Writing your own middleware + +You can also write you own middleware by implementing [`tower::Service`]: + +``` +use axum::{ + body::{Body, BoxBody}, + routing::get, + http::{Request, Response}, + Router, +}; +use futures::future::BoxFuture; +use tower::{Service, layer::layer_fn}; +use std::task::{Context, Poll}; + +#[derive(Clone)] +struct MyMiddleware { + inner: S, +} + +impl Service> for MyMiddleware +where + S: Service, Response = Response> + Clone + Send + 'static, + S::Future: Send + 'static, + ReqBody: Send + 'static, + ResBody: Send + 'static, +{ + type Response = S::Response; + type Error = S::Error; + type Future = BoxFuture<'static, Result>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_ready(cx) + } + + fn call(&mut self, mut req: Request) -> Self::Future { + println!("`MyMiddleware` called!"); + + // best practice is to clone the inner service like this + // see https://github.com/tower-rs/tower/issues/547 for details + let clone = self.inner.clone(); + let mut inner = std::mem::replace(&mut self.inner, clone); + + Box::pin(async move { + let res: Response = inner.call(req).await?; + + println!("`MyMiddleware` received the response"); + + Ok(res) + }) + } +} + +let app = Router::new() + .route("/", get(|| async { /* ... */ })) + .layer(layer_fn(|inner| MyMiddleware { inner })); +# async { +# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); +# }; +``` diff --git a/src/docs/building_responses.md b/src/docs/building_responses.md new file mode 100644 index 00000000..b8e20b44 --- /dev/null +++ b/src/docs/building_responses.md @@ -0,0 +1,113 @@ +# Building responses + +Anything that implements [`IntoResponse`](response::IntoResponse) can be +returned from a handler: + +```rust,no_run +use axum::{ + body::Body, + routing::get, + handler::Handler, + http::{Request, header::{HeaderMap, HeaderName, HeaderValue}}, + response::{IntoResponse, Html, Json, Headers}, + Router, +}; +use http::{StatusCode, Response, Uri}; +use serde_json::{Value, json}; + +// We've already seen returning &'static str +async fn plain_text() -> &'static str { + "foo" +} + +// String works too and will get a `text/plain` content-type +async fn plain_text_string(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] +} + +// `()` gives an empty response +async fn empty() {} + +// `StatusCode` gives an empty response with that status code +async fn empty_with_status() -> StatusCode { + StatusCode::NOT_FOUND +} + +// A tuple of `StatusCode` and something that implements `IntoResponse` can +// be used to override the status code +async fn with_status() -> (StatusCode, &'static str) { + (StatusCode::INTERNAL_SERVER_ERROR, "Something went wrong") +} + +// A tuple of `HeaderMap` and something that implements `IntoResponse` can +// be used to override the headers +async fn with_headers() -> (HeaderMap, &'static str) { + let mut headers = HeaderMap::new(); + headers.insert( + HeaderName::from_static("x-foo"), + HeaderValue::from_static("foo"), + ); + (headers, "foo") +} + +// You can also override both status and headers at the same time +async fn with_headers_and_status() -> (StatusCode, HeaderMap, &'static str) { + let mut headers = HeaderMap::new(); + headers.insert( + HeaderName::from_static("x-foo"), + HeaderValue::from_static("foo"), + ); + (StatusCode::INTERNAL_SERVER_ERROR, headers, "foo") +} + +// `Headers` makes building the header map easier and `impl Trait` is easier +// so you don't have to write the whole type +async fn with_easy_headers() -> impl IntoResponse { + Headers(vec![("x-foo", "foo")]) +} + +// `Html` gives a content-type of `text/html` +async fn html() -> Html<&'static str> { + Html("

Hello, World!

") +} + +// `Json` gives a content-type of `application/json` and works with any type +// that implements `serde::Serialize` +async fn json() -> Json { + Json(json!({ "data": 42 })) +} + +// `Result` where `T` and `E` implement `IntoResponse` is useful for +// returning errors +async fn result() -> Result<&'static str, StatusCode> { + Ok("all good") +} + +// `Response` gives full control +async fn response() -> Response { + Response::builder().body(Body::empty()).unwrap() +} + +let app = Router::new() + .route("/plain_text", get(plain_text)) + .route("/plain_text_string", get(plain_text_string)) + .route("/bytes", get(bytes)) + .route("/empty", get(empty)) + .route("/empty_with_status", get(empty_with_status)) + .route("/with_status", get(with_status)) + .route("/with_headers", get(with_headers)) + .route("/with_headers_and_status", get(with_headers_and_status)) + .route("/with_easy_headers", get(with_easy_headers)) + .route("/html", get(html)) + .route("/json", get(json)) + .route("/result", get(result)) + .route("/response", get(response)); +# async { +# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); +# }; +``` diff --git a/src/docs/compatibility.md b/src/docs/compatibility.md new file mode 100644 index 00000000..4634e385 --- /dev/null +++ b/src/docs/compatibility.md @@ -0,0 +1,4 @@ +# Compatibility + +axum is designed to work with [tokio] and [hyper]. Runtime and +transport layer independence is not a goal, at least for the time being. diff --git a/src/docs/error_handling.md b/src/docs/error_handling.md new file mode 100644 index 00000000..1358848b --- /dev/null +++ b/src/docs/error_handling.md @@ -0,0 +1,19 @@ +# Error handling + +In the context of axum an "error" specifically means if a [`Service`]'s +response future resolves to `Err(Service::Error)`. That means async handler +functions can _never_ fail since they always produce a response and their +`Service::Error` type is [`Infallible`]. Returning statuses like 404 or 500 +are _not_ errors. + +axum works this way because hyper will close the connection, without sending +a response, if an error is encountered. This is not desireable so axum makes +it impossible to forget to handle errors. + +Sometimes you need to route to fallible services or apply fallible +middleware in which case you need to handle the errors. That can be done +using things from [`error_handling`]. + +You can find examples here: +- [Routing to fallible services](#routing-to-fallible-services) +- [Applying fallible middleware](#applying-multiple-middleware) diff --git a/src/docs/example.md b/src/docs/example.md new file mode 100644 index 00000000..ccc060d8 --- /dev/null +++ b/src/docs/example.md @@ -0,0 +1,22 @@ +# Example + +The "Hello, World!" of axum is: + +```rust,no_run +use axum::{ + routing::get, + Router, +}; + +#[tokio::main] +async fn main() { + // build our application with a single route + let app = Router::new().route("/", get(|| async { "Hello, World!" })); + + // run it with hyper on localhost:3000 + axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) + .serve(app.into_make_service()) + .await + .unwrap(); +} +``` diff --git a/src/docs/examples.md b/src/docs/examples.md new file mode 100644 index 00000000..e27d4c5b --- /dev/null +++ b/src/docs/examples.md @@ -0,0 +1,4 @@ +# Examples + +The axum repo contains [a number of examples][examples] that show how to put all the +pieces together. diff --git a/src/docs/extractors.md b/src/docs/extractors.md new file mode 100644 index 00000000..b1f61e94 --- /dev/null +++ b/src/docs/extractors.md @@ -0,0 +1,270 @@ +# Extractors + +An extractor is a type that implements [`FromRequest`]. Extractors is how +you pick apart the incoming request to get the parts your handler needs. + +For example, [`extract::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, + Router, +}; +use serde::Deserialize; + +let app = Router::new().route("/users", post(create_user)); + +#[derive(Deserialize)] +struct CreateUser { + email: String, + password: String, +} + +async fn create_user(Json(payload): Json) { + // ... +} +# async { +# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); +# }; +``` + +See the [`extract`] module for everything that can be used as an extractor. + +## Common extractors + +Some commonly used extractors are: + +```rust,no_run +use axum::{ + extract::{Json, TypedHeader, Path, Extension, Query}, + routing::post, + http::{Request, header::HeaderMap}, + body::{Bytes, Body}, + Router, +}; +use serde_json::Value; +use headers::UserAgent; +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) {} + +// `TypedHeader` can be used to extract a single header +// note this requires you've enabled axum's `headers` +async fn user_agent(TypedHeader(user_agent): TypedHeader) {} + +// `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" +// See the "Sharing state with handlers" section for more details +async fn extension(Extension(state): Extension) {} + +#[derive(Clone)] +struct State { /* ... */ } + +let app = Router::new() + .route("/path", post(path)) + .route("/query", post(query)) + .route("/user_agent", post(user_agent)) + .route("/headers", post(headers)) + .route("/string", post(string)) + .route("/bytes", post(bytes)) + .route("/json", post(json)) + .route("/request", post(request)) + .route("/extension", post(extension)); +# async { +# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); +# }; +``` + +## Applying multiple extractors + +You can also apply multiple extractors: + +```rust,no_run +use axum::{ + extract, + 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, +} + +impl Default for Pagination { + fn default() -> Self { + Self { page: 1, per_page: 30 } + } +} + +async fn get_user_things( + extract::Path(user_id): extract::Path, + pagination: Option>, +) { + let pagination: Pagination = pagination.unwrap_or_default().0; + + // ... +} +# async { +# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); +# }; +``` + +Take care of the order in which you apply extractors as some extractors +mutate the request. + +For example using [`HeaderMap`] as an extractor will make the headers +inaccessible for other extractors on the handler. If you need to extract +individual headers _and_ a [`HeaderMap`] make sure to apply the extractor of +individual headers first: + +```rust,no_run +use axum::{ + extract::TypedHeader, + routing::get, + http::header::HeaderMap, + Router, +}; +use headers::UserAgent; + +async fn handler( + TypedHeader(user_agent): TypedHeader, + all_headers: HeaderMap, +) { + // ... +} + +let app = Router::new().route("/", get(handler)); +# async { +# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); +# }; +``` + +Extractors that consume the request body can also only be applied once as +well as [`Request`], which consumes the entire request: + +```rust,no_run +use axum::{ + routing::get, + http::Request, + body::Body, + Router, +}; + +async fn handler(request: Request) { + // ... +} + +let app = Router::new().route("/", get(handler)); +# async { +# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); +# }; +``` + +Extractors always run in the order of the function parameters that is from +left to right. + +## Optional extractors + +All extractors defined in axum will reject the request if it doesn't match. +If you wish to make an extractor optional you can wrap it in `Option`: + +```rust,no_run +use axum::{ + extract::Json, + routing::post, + Router, +}; +use serde_json::Value; + +async fn create_user(payload: Option>) { + if let Some(payload) = payload { + // We got a valid JSON payload + } else { + // Payload wasn't valid JSON + } +} + +let app = Router::new().route("/users", post(create_user)); +# async { +# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); +# }; +``` + +Wrapping extractors in `Result` makes them optional and gives you the reason +the extraction failed: + +```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::InvalidJsonBody(_)) => { + // Couldn't deserialize the body into the target type + } + Err(JsonRejection::BodyAlreadyExtracted(_)) => { + // Another extractor had already consumed the body + } + Err(_) => { + // `JsonRejection` is marked `#[non_exhaustive]` so match must + // include a catch-all case. + } + } +} + +let app = Router::new().route("/users", post(create_user)); +# async { +# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); +# }; +``` + +## 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 a two +options: + +1. Use `Result` as your extractor like shown in ["Optional + extractors"](#optional-extractors). This works well if you're only using + the extractor in a single handler. +2. Create your own extractor that in its [`FromRequest`] implemention calls + one of axum's built in extractors but returns a different response for + rejections. See the [customize-extractor-error] example for more details. diff --git a/src/docs/feature_flags.md b/src/docs/feature_flags.md new file mode 100644 index 00000000..090261eb --- /dev/null +++ b/src/docs/feature_flags.md @@ -0,0 +1,15 @@ +# Feature flags + +axum uses a set of [feature flags] to reduce the amount of compiled and +optional dependencies. + +The following optional features are available: + +- `headers`: Enables extracting typed headers via [`extract::TypedHeader`]. +- `http1`: Enables hyper's `http1` feature. Enabled by default. +- `http2`: Enables hyper's `http2` feature. +- `json`: Enables the [`Json`] type and some similar convenience functionality. + Enabled by default. +- `multipart`: Enables parsing `multipart/form-data` requests with [`extract::Multipart`]. +- `tower-log`: Enables `tower`'s `log` feature. Enabled by default. +- `ws`: Enables WebSockets support via [`extract::ws`]. diff --git a/src/docs/handlers.md b/src/docs/handlers.md new file mode 100644 index 00000000..18d91975 --- /dev/null +++ b/src/docs/handlers.md @@ -0,0 +1,68 @@ +# Handlers + +In axum a "handler" is an async function that accepts zero or more +["extractors"](#extractors) as arguments and returns something that +can be converted [into a response](#building-responses). + +Handlers is where your custom domain logic lives and axum applications are +built by routing between handlers. + +Some examples of handlers: + +```rust +use bytes::Bytes; +use http::StatusCode; + +// Handler that immediately returns an empty `200 OK` response. +async fn unit_handler() {} + +// Handler that immediately returns an empty `200 OK` response with a plain +// text body. +async fn string_handler() -> String { + "Hello, World!".to_string() +} + +// Handler that buffers the request body and returns it. +async fn echo(body: Bytes) -> Result { + if let Ok(string) = String::from_utf8(body.to_vec()) { + Ok(string) + } else { + Err(StatusCode::BAD_REQUEST) + } +} +``` + +## Debugging handler type errors + +For a function to 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 [`FromRequest`]. +- Returns something that implements [`IntoResponse`]. +- If a closure is used it must implement `Clone + Send + Sync` 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-debug] crate. diff --git a/src/docs/high_level_features.md b/src/docs/high_level_features.md new file mode 100644 index 00000000..6755fa7e --- /dev/null +++ b/src/docs/high_level_features.md @@ -0,0 +1,14 @@ +# High level features + +- Route requests to handlers with a macro free API. +- Declaratively parse requests using extractors. +- Simple and predictable error handling model. +- Generate responses with minimal boilerplate. +- Take full advantage of the [`tower`] and [`tower-http`] ecosystem of + middleware, services, and utilities. + +In particular the last point is what sets `axum` apart from other frameworks. +`axum` doesn't have its own middleware system but instead uses +[`tower::Service`]. This means `axum` gets timeouts, tracing, compression, +authorization, and more, for free. It also enables you to share middleware with +applications written using [`hyper`] or [`tonic`]. diff --git a/src/docs/required_dependencies.md b/src/docs/required_dependencies.md new file mode 100644 index 00000000..f87540de --- /dev/null +++ b/src/docs/required_dependencies.md @@ -0,0 +1,20 @@ +# Required dependencies + +To use axum there are a few dependencies you have pull in as well: + +```toml +[dependencies] +axum = "" +hyper = { version = "", features = ["full"] } +tokio = { version = "", features = ["full"] } +tower = "" +``` + +The `"full"` feature for hyper and tokio isn't strictly necessary but its +the easiest way to get started. + +Note that [`hyper::Server`] is re-exported by axum so if thats all you need +then you don't have to explicitly depend on hyper. + +Tower isn't strictly necessary either but helpful for testing. See the +testing example in the repo to learn more about testing axum apps. diff --git a/src/docs/routing.md b/src/docs/routing.md new file mode 100644 index 00000000..ca4b662e --- /dev/null +++ b/src/docs/routing.md @@ -0,0 +1,286 @@ +# Routing + +[`Router::route`] is the main way to add routes: + +```rust,no_run +use axum::{ + routing::get, + Router, +}; + +let app = Router::new() + .route("/", get(get_slash).post(post_slash)) + .route("/foo", get(get_foo)); + +async fn get_slash() { + // `GET /` called +} + +async fn post_slash() { + // `POST /` called +} + +async fn get_foo() { + // `GET /foo` called +} +# async { +# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); +# }; +``` + +Routes can also be dynamic like `/users/:id`. See [extractors](#extractors) +for more details. + +You can also define routes separately and merge them with [`Router::merge`]. + +Routes are not allowed to overlap and will panic if an overlapping route is +added. This also means the order in which routes are added doesn't matter. + +## Wildcard routes + +axum also supports wildcard routes: + +```rust,no_run +use axum::{ + routing::get, + Router, +}; + +let app = Router::new() + // this matches any request that starts with `/api` + .route("/api/*rest", get(|| async { /* ... */ })); +# async { +# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); +# }; +``` + +The matched path can be extracted via [`extract::Path`]: + +```rust,no_run +use axum::{ + routing::get, + extract::Path, + Router, +}; + +let app = Router::new().route("/api/*rest", get(|Path(rest): Path| async { + // `rest` will be everything after `/api` +})); +# async { +# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); +# }; +``` + +## Nesting routes + +Routes can be nested by calling [`Router::nest`](routing::Router::nest): + +```rust,no_run +use axum::{ + body::{Body, BoxBody}, + http::Request, + routing::get, + Router, +}; +use tower_http::services::ServeFile; +use http::Response; + +fn api_routes() -> Router { + Router::new() + .route("/users", get(|_: Request| async { /* ... */ })) +} + +let app = Router::new() + .route("/", get(|_: Request| async { /* ... */ })) + .nest("/api", api_routes()); +# async { +# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); +# }; +``` + +Note that nested routes will not see the orignal 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. + +Nested routes are similar to wild card 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 app = Router::new() + .route("/foo/*rest", get(|uri: Uri| async { + // `uri` will contain `/foo` + })) + .nest("/bar", get(|uri: Uri| async { + // `uri` will _not_ contain `/bar` + })); +# async { +# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); +# }; +``` + +## Fallback routes + +By default axum will respond with an empty `404 Not Found` response to unhandled requests. To +override that you can use [`Router::fallback`]: + +```rust +use axum::{ + Router, + routing::get, + handler::Handler, + response::IntoResponse, + http::{StatusCode, Uri}, +}; + +async fn fallback(uri: Uri) -> impl IntoResponse { + (StatusCode::NOT_FOUND, format!("No route for {}", uri)) +} + +let app = Router::new() + .route("/foo", get(|| async { /* ... */ })) + .fallback(fallback.into_service()); +# async { +# hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); +# }; +``` + +See [`Router::fallback`] for more details. + +## Routing to any [`Service`] + +axum also supports routing to general [`Service`]s: + +```rust,no_run +use axum::{ + Router, + body::Body, + routing::service_method_router as service, + error_handling::HandleErrorExt, + http::{Request, StatusCode}, +}; +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 who's response body is not `axum::body::BoxBody` + // can be wrapped in `axum::service::any` (or one of the other routing filters) + // to have the response body mapped + service::any(service_fn(|_: Request| async { + let res = Response::new(Body::from("Hi from `GET /`")); + Ok::<_, Infallible>(res) + })) + ) + .route( + "/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 body = axum::body::box_body(body); + let res = Response::new(body); + Ok::<_, Infallible>(res) + }) + ) + .route( + // GET `/static/Cargo.toml` goes to a service from tower-http + "/static/Cargo.toml", + service::get(ServeFile::new("Cargo.toml")) + // though we must handle any potential errors + .handle_error(|error: io::Error| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Unhandled internal error: {}", error), + ) + }) + ); +# async { +# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); +# }; +``` + +Routing to arbitrary services in this way has complications for backpressure +([`Service::poll_ready`]). See the [`service`] module for more details. + +### Routing to fallible services + +Note that routing to general services has a small gotcha when it comes to +errors. axum currently does not support mixing routes to fallible services +with infallible handlers. For example this does _not_ compile: + +```compile_fail +use axum::{ + Router, + routing::{get, service_method_router as service}, + http::{Request, Response}, + body::Body, +}; +use std::io; +use tower::service_fn; + +let app = Router::new() + // this route cannot fail + .route("/foo", get(|| async {})) + // this route can fail with io::Error + .route( + "/", + service::get(service_fn(|_req: Request| async { + let contents = tokio::fs::read_to_string("some_file").await?; + Ok::<_, io::Error>(Response::new(Body::from(contents))) + })), + ); +# async { +# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); +# }; +``` + +The solution is to use [`handle_error`] and handle the error from the +service: + +``` +use axum::{ + Router, + body::Body, + routing::{get, service_method_router as service}, + response::IntoResponse, + http::{Request, Response}, + error_handling::HandleErrorExt, +}; +use std::{io, convert::Infallible}; +use tower::service_fn; + +let app = Router::new() + // this route cannot fail + .route("/foo", get(|| async {})) + // this route can fail with io::Error + .route( + "/", + service::get(service_fn(|_req: Request| async { + let contents = tokio::fs::read_to_string("some_file").await?; + Ok::<_, io::Error>(Response::new(Body::from(contents))) + })) + .handle_error(handle_io_error), + ); + +fn handle_io_error(error: io::Error) -> impl IntoResponse { + // ... +} +# async { +# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); +# }; +``` + +In this particular case you can also handle the error directly in +`service_fn` but that is not possible, if you're routing to a service which +you don't control. + +See ["Error handling"](#error-handling) for more details on [`handle_error`] +and error handling in general. diff --git a/src/docs/sharing_state_with_handlers.md b/src/docs/sharing_state_with_handlers.md new file mode 100644 index 00000000..e1266649 --- /dev/null +++ b/src/docs/sharing_state_with_handlers.md @@ -0,0 +1,37 @@ +# Sharing state with handlers + +It is common to share some state between handlers for example to share a +pool of database connections or clients to other services. That can be done +using the [`AddExtension`] middleware (applied with [`AddExtensionLayer`]) +and the [`extract::Extension`] extractor: + +```rust,no_run +use axum::{ + AddExtensionLayer, + extract, + routing::get, + Router, +}; +use std::sync::Arc; + +struct State { + // ... +} + +let shared_state = Arc::new(State { /* ... */ }); + +let app = Router::new() + .route("/", get(handler)) + .layer(AddExtensionLayer::new(shared_state)); + +async fn handler( + state: extract::Extension>, +) { + let state: Arc = state.0; + + // ... +} +# async { +# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); +# }; +``` diff --git a/src/docs/table_of_contents.md b/src/docs/table_of_contents.md new file mode 100644 index 00000000..bf3d5d98 --- /dev/null +++ b/src/docs/table_of_contents.md @@ -0,0 +1,29 @@ +# Table of contents + +- [High level features](#high-level-features) +- [Compatibility](#compatibility) +- [Handlers](#handlers) + - [Debugging handler type errors](#debugging-handler-type-errors) +- [Routing](#routing) + - [Wildcard routes](#wildcard-routes) + - [Nesting routes](#nesting-routes) + - [Fallback routes](#fallback-routes) + - [Routing to any `Service`](#routing-to-any-service) + - [Routing to fallible services](#routing-to-fallible-services) +- [Extractors](#extractors) + - [Common extractors](#common-extractors) + - [Applying multiple extractors](#applying-multiple-extractors) + - [Optional extractors](#optional-extractors) + - [Customizing extractor responses](#customizing-extractor-responses) +- [Building responses](#building-responses) +- [Error handling](#error-handling) +- [Applying middleware](#applying-middleware) + - [To individual handlers](#to-individual-handlers) + - [To groups of routes](#to-groups-of-routes) + - [Applying multiple middleware](#applying-multiple-middleware) + - [Commonly used middleware](#commonly-used-middleware) + - [Writing your own middleware](#writing-your-own-middleware) +- [Sharing state with handlers](#sharing-state-with-handlers) +- [Required dependencies](#required-dependencies) +- [Examples](#examples) +- [Feature flags](#feature-flags) diff --git a/src/lib.rs b/src/lib.rs index 0dc1ca3b..4670f48b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,1172 +1,19 @@ //! axum is a web application framework that focuses on ergonomics and modularity. //! -//! # Table of contents -//! -//! - [High level features](#high-level-features) -//! - [Compatibility](#compatibility) -//! - [Handlers](#handlers) -//! - [Debugging handler type errors](#debugging-handler-type-errors) -//! - [Routing](#routing) -//! - [Wildcard routes](#wildcard-routes) -//! - [Nesting routes](#nesting-routes) -//! - [Fallback routes](#fallback-routes) -//! - [Routing to any `Service`](#routing-to-any-service) -//! - [Routing to fallible services](#routing-to-fallible-services) -//! - [Extractors](#extractors) -//! - [Common extractors](#common-extractors) -//! - [Applying multiple extractors](#applying-multiple-extractors) -//! - [Optional extractors](#optional-extractors) -//! - [Customizing extractor responses](#customizing-extractor-responses) -//! - [Building responses](#building-responses) -//! - [Error handling](#error-handling) -//! - [Applying middleware](#applying-middleware) -//! - [To individual handlers](#to-individual-handlers) -//! - [To groups of routes](#to-groups-of-routes) -//! - [Applying multiple middleware](#applying-multiple-middleware) -//! - [Commonly used middleware](#commonly-used-middleware) -//! - [Writing your own middleware](#writing-your-own-middleware) -//! - [Sharing state with handlers](#sharing-state-with-handlers) -//! - [Required dependencies](#required-dependencies) -//! - [Examples](#examples) -//! - [Feature flags](#feature-flags) -//! -//! # High level features -//! -//! - Route requests to handlers with a macro free API. -//! - Declaratively parse requests using extractors. -//! - Simple and predictable error handling model. -//! - Generate responses with minimal boilerplate. -//! - Take full advantage of the [`tower`] and [`tower-http`] ecosystem of -//! middleware, services, and utilities. -//! -//! In particular the last point is what sets `axum` apart from other frameworks. -//! `axum` doesn't have its own middleware system but instead uses -//! [`tower::Service`]. This means `axum` gets timeouts, tracing, compression, -//! authorization, and more, for free. It also enables you to share middleware with -//! applications written using [`hyper`] or [`tonic`]. -//! -//! # Compatibility -//! -//! axum is designed to work with [tokio] and [hyper]. Runtime and -//! transport layer independence is not a goal, at least for the time being. -//! -//! # Example -//! -//! The "Hello, World!" of axum is: -//! -//! ```rust,no_run -//! use axum::{ -//! routing::get, -//! Router, -//! }; -//! -//! #[tokio::main] -//! async fn main() { -//! // build our application with a single route -//! let app = Router::new().route("/", get(|| async { "Hello, World!" })); -//! -//! // run it with hyper on localhost:3000 -//! axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) -//! .serve(app.into_make_service()) -//! .await -//! .unwrap(); -//! } -//! ``` -//! -//! # Handlers -//! -//! In axum a "handler" is an async function that accepts zero or more -//! ["extractors"](#extractors) as arguments and returns something that -//! can be converted [into a response](#building-responses). -//! -//! Handlers is where your custom domain logic lives and axum applications are -//! built by routing between handlers. -//! -//! Some examples of handlers: -//! -//! ```rust -//! use bytes::Bytes; -//! use http::StatusCode; -//! -//! // Handler that immediately returns an empty `200 OK` response. -//! async fn unit_handler() {} -//! -//! // Handler that immediately returns an empty `200 OK` response with a plain -//! // text body. -//! async fn string_handler() -> String { -//! "Hello, World!".to_string() -//! } -//! -//! // Handler that buffers the request body and returns it. -//! async fn echo(body: Bytes) -> Result { -//! if let Ok(string) = String::from_utf8(body.to_vec()) { -//! Ok(string) -//! } else { -//! Err(StatusCode::BAD_REQUEST) -//! } -//! } -//! ``` -//! -//! ## Debugging handler type errors -//! -//! For a function to 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 [`FromRequest`]. -//! - Returns something that implements [`IntoResponse`]. -//! - If a closure is used it must implement `Clone + Send + Sync` 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-debug] crate. -//! -//! # Routing -//! -//! [`Router::route`] is the main way to add routes: -//! -//! ```rust,no_run -//! use axum::{ -//! routing::get, -//! Router, -//! }; -//! -//! let app = Router::new() -//! .route("/", get(get_slash).post(post_slash)) -//! .route("/foo", get(get_foo)); -//! -//! async fn get_slash() { -//! // `GET /` called -//! } -//! -//! async fn post_slash() { -//! // `POST /` called -//! } -//! -//! async fn get_foo() { -//! // `GET /foo` called -//! } -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; -//! ``` -//! -//! Routes can also be dynamic like `/users/:id`. See [extractors](#extractors) -//! for more details. -//! -//! You can also define routes separately and merge them with [`Router::merge`]. -//! -//! Routes are not allowed to overlap and will panic if an overlapping route is -//! added. This also means the order in which routes are added doesn't matter. -//! -//! ## Wildcard routes -//! -//! axum also supports wildcard routes: -//! -//! ```rust,no_run -//! use axum::{ -//! routing::get, -//! Router, -//! }; -//! -//! let app = Router::new() -//! // this matches any request that starts with `/api` -//! .route("/api/*rest", get(|| async { /* ... */ })); -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; -//! ``` -//! -//! The matched path can be extracted via [`extract::Path`]: -//! -//! ```rust,no_run -//! use axum::{ -//! routing::get, -//! extract::Path, -//! Router, -//! }; -//! -//! let app = Router::new().route("/api/*rest", get(|Path(rest): Path| async { -//! // `rest` will be everything after `/api` -//! })); -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; -//! ``` -//! -//! ## Nesting routes -//! -//! Routes can be nested by calling [`Router::nest`](routing::Router::nest): -//! -//! ```rust,no_run -//! use axum::{ -//! body::{Body, BoxBody}, -//! http::Request, -//! routing::get, -//! Router, -//! }; -//! use tower_http::services::ServeFile; -//! use http::Response; -//! -//! fn api_routes() -> Router { -//! Router::new() -//! .route("/users", get(|_: Request| async { /* ... */ })) -//! } -//! -//! let app = Router::new() -//! .route("/", get(|_: Request| async { /* ... */ })) -//! .nest("/api", api_routes()); -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; -//! ``` -//! -//! Note that nested routes will not see the orignal 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. -//! -//! Nested routes are similar to wild card 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 app = Router::new() -//! .route("/foo/*rest", get(|uri: Uri| async { -//! // `uri` will contain `/foo` -//! })) -//! .nest("/bar", get(|uri: Uri| async { -//! // `uri` will _not_ contain `/bar` -//! })); -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; -//! ``` -//! -//! ## Fallback routes -//! -//! By default axum will respond with an empty `404 Not Found` response to unhandled requests. To -//! override that you can use [`Router::fallback`]: -//! -//! ```rust -//! use axum::{ -//! Router, -//! routing::get, -//! handler::Handler, -//! response::IntoResponse, -//! http::{StatusCode, Uri}, -//! }; -//! -//! async fn fallback(uri: Uri) -> impl IntoResponse { -//! (StatusCode::NOT_FOUND, format!("No route for {}", uri)) -//! } -//! -//! let app = Router::new() -//! .route("/foo", get(|| async { /* ... */ })) -//! .fallback(fallback.into_service()); -//! # async { -//! # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; -//! ``` -//! -//! See [`Router::fallback`] for more details. -//! -//! ## Routing to any [`Service`] -//! -//! axum also supports routing to general [`Service`]s: -//! -//! ```rust,no_run -//! use axum::{ -//! Router, -//! body::Body, -//! routing::service_method_router as service, -//! error_handling::HandleErrorExt, -//! http::{Request, StatusCode}, -//! }; -//! 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 who's response body is not `axum::body::BoxBody` -//! // can be wrapped in `axum::service::any` (or one of the other routing filters) -//! // to have the response body mapped -//! service::any(service_fn(|_: Request| async { -//! let res = Response::new(Body::from("Hi from `GET /`")); -//! Ok::<_, Infallible>(res) -//! })) -//! ) -//! .route( -//! "/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 body = axum::body::box_body(body); -//! let res = Response::new(body); -//! Ok::<_, Infallible>(res) -//! }) -//! ) -//! .route( -//! // GET `/static/Cargo.toml` goes to a service from tower-http -//! "/static/Cargo.toml", -//! service::get(ServeFile::new("Cargo.toml")) -//! // though we must handle any potential errors -//! .handle_error(|error: io::Error| { -//! ( -//! StatusCode::INTERNAL_SERVER_ERROR, -//! format!("Unhandled internal error: {}", error), -//! ) -//! }) -//! ); -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; -//! ``` -//! -//! Routing to arbitrary services in this way has complications for backpressure -//! ([`Service::poll_ready`]). See the [`service`] module for more details. -//! -//! ### Routing to fallible services -//! -//! Note that routing to general services has a small gotcha when it comes to -//! errors. axum currently does not support mixing routes to fallible services -//! with infallible handlers. For example this does _not_ compile: -//! -//! ```compile_fail -//! use axum::{ -//! Router, -//! routing::{get, service_method_router as service}, -//! http::{Request, Response}, -//! body::Body, -//! }; -//! use std::io; -//! use tower::service_fn; -//! -//! let app = Router::new() -//! // this route cannot fail -//! .route("/foo", get(|| async {})) -//! // this route can fail with io::Error -//! .route( -//! "/", -//! service::get(service_fn(|_req: Request| async { -//! let contents = tokio::fs::read_to_string("some_file").await?; -//! Ok::<_, io::Error>(Response::new(Body::from(contents))) -//! })), -//! ); -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; -//! ``` -//! -//! The solution is to use [`handle_error`] and handle the error from the -//! service: -//! -//! ``` -//! use axum::{ -//! Router, -//! body::Body, -//! routing::{get, service_method_router as service}, -//! response::IntoResponse, -//! http::{Request, Response}, -//! error_handling::HandleErrorExt, -//! }; -//! use std::{io, convert::Infallible}; -//! use tower::service_fn; -//! -//! let app = Router::new() -//! // this route cannot fail -//! .route("/foo", get(|| async {})) -//! // this route can fail with io::Error -//! .route( -//! "/", -//! service::get(service_fn(|_req: Request| async { -//! let contents = tokio::fs::read_to_string("some_file").await?; -//! Ok::<_, io::Error>(Response::new(Body::from(contents))) -//! })) -//! .handle_error(handle_io_error), -//! ); -//! -//! fn handle_io_error(error: io::Error) -> impl IntoResponse { -//! // ... -//! } -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; -//! ``` -//! -//! In this particular case you can also handle the error directly in -//! `service_fn` but that is not possible, if you're routing to a service which -//! you don't control. -//! -//! See ["Error handling"](#error-handling) for more details on [`handle_error`] -//! and error handling in general. -//! -//! # Extractors -//! -//! An extractor is a type that implements [`FromRequest`]. Extractors is how -//! you pick apart the incoming request to get the parts your handler needs. -//! -//! For example, [`extract::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, -//! Router, -//! }; -//! use serde::Deserialize; -//! -//! let app = Router::new().route("/users", post(create_user)); -//! -//! #[derive(Deserialize)] -//! struct CreateUser { -//! email: String, -//! password: String, -//! } -//! -//! async fn create_user(Json(payload): Json) { -//! // ... -//! } -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; -//! ``` -//! -//! See the [`extract`] module for everything that can be used as an extractor. -//! -//! ## Common extractors -//! -//! Some commonly used extractors are: -//! -//! ```rust,no_run -//! use axum::{ -//! extract::{Json, TypedHeader, Path, Extension, Query}, -//! routing::post, -//! http::{Request, header::HeaderMap}, -//! body::{Bytes, Body}, -//! Router, -//! }; -//! use serde_json::Value; -//! use headers::UserAgent; -//! 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) {} -//! -//! // `TypedHeader` can be used to extract a single header -//! // note this requires you've enabled axum's `headers` -//! async fn user_agent(TypedHeader(user_agent): TypedHeader) {} -//! -//! // `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" -//! // See the "Sharing state with handlers" section for more details -//! async fn extension(Extension(state): Extension) {} -//! -//! #[derive(Clone)] -//! struct State { /* ... */ } -//! -//! let app = Router::new() -//! .route("/path", post(path)) -//! .route("/query", post(query)) -//! .route("/user_agent", post(user_agent)) -//! .route("/headers", post(headers)) -//! .route("/string", post(string)) -//! .route("/bytes", post(bytes)) -//! .route("/json", post(json)) -//! .route("/request", post(request)) -//! .route("/extension", post(extension)); -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; -//! ``` -//! -//! ## Applying multiple extractors -//! -//! You can also apply multiple extractors: -//! -//! ```rust,no_run -//! use axum::{ -//! extract, -//! 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, -//! } -//! -//! impl Default for Pagination { -//! fn default() -> Self { -//! Self { page: 1, per_page: 30 } -//! } -//! } -//! -//! async fn get_user_things( -//! extract::Path(user_id): extract::Path, -//! pagination: Option>, -//! ) { -//! let pagination: Pagination = pagination.unwrap_or_default().0; -//! -//! // ... -//! } -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; -//! ``` -//! -//! Take care of the order in which you apply extractors as some extractors -//! mutate the request. -//! -//! For example using [`HeaderMap`] as an extractor will make the headers -//! inaccessible for other extractors on the handler. If you need to extract -//! individual headers _and_ a [`HeaderMap`] make sure to apply the extractor of -//! individual headers first: -//! -//! ```rust,no_run -//! use axum::{ -//! extract::TypedHeader, -//! routing::get, -//! http::header::HeaderMap, -//! Router, -//! }; -//! use headers::UserAgent; -//! -//! async fn handler( -//! TypedHeader(user_agent): TypedHeader, -//! all_headers: HeaderMap, -//! ) { -//! // ... -//! } -//! -//! let app = Router::new().route("/", get(handler)); -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; -//! ``` -//! -//! Extractors that consume the request body can also only be applied once as -//! well as [`Request`], which consumes the entire request: -//! -//! ```rust,no_run -//! use axum::{ -//! routing::get, -//! http::Request, -//! body::Body, -//! Router, -//! }; -//! -//! async fn handler(request: Request) { -//! // ... -//! } -//! -//! let app = Router::new().route("/", get(handler)); -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; -//! ``` -//! -//! Extractors always run in the order of the function parameters that is from -//! left to right. -//! -//! ## Optional extractors -//! -//! All extractors defined in axum will reject the request if it doesn't match. -//! If you wish to make an extractor optional you can wrap it in `Option`: -//! -//! ```rust,no_run -//! use axum::{ -//! extract::Json, -//! routing::post, -//! Router, -//! }; -//! use serde_json::Value; -//! -//! async fn create_user(payload: Option>) { -//! if let Some(payload) = payload { -//! // We got a valid JSON payload -//! } else { -//! // Payload wasn't valid JSON -//! } -//! } -//! -//! let app = Router::new().route("/users", post(create_user)); -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; -//! ``` -//! -//! Wrapping extractors in `Result` makes them optional and gives you the reason -//! the extraction failed: -//! -//! ```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::InvalidJsonBody(_)) => { -//! // Couldn't deserialize the body into the target type -//! } -//! Err(JsonRejection::BodyAlreadyExtracted(_)) => { -//! // Another extractor had already consumed the body -//! } -//! Err(_) => { -//! // `JsonRejection` is marked `#[non_exhaustive]` so match must -//! // include a catch-all case. -//! } -//! } -//! } -//! -//! let app = Router::new().route("/users", post(create_user)); -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; -//! ``` -//! -//! ## 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 a two -//! options: -//! -//! 1. Use `Result` as your extractor like shown in ["Optional -//! extractors"](#optional-extractors). This works well if you're only using -//! the extractor in a single handler. -//! 2. Create your own extractor that in its [`FromRequest`] implemention calls -//! one of axum's built in extractors but returns a different response for -//! rejections. See the [customize-extractor-error] example for more details. -//! -//! # Building responses -//! -//! Anything that implements [`IntoResponse`](response::IntoResponse) can be -//! returned from a handler: -//! -//! ```rust,no_run -//! use axum::{ -//! body::Body, -//! routing::get, -//! handler::Handler, -//! http::{Request, header::{HeaderMap, HeaderName, HeaderValue}}, -//! response::{IntoResponse, Html, Json, Headers}, -//! Router, -//! }; -//! use http::{StatusCode, Response, Uri}; -//! use serde_json::{Value, json}; -//! -//! // We've already seen returning &'static str -//! async fn plain_text() -> &'static str { -//! "foo" -//! } -//! -//! // String works too and will get a `text/plain` content-type -//! async fn plain_text_string(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] -//! } -//! -//! // `()` gives an empty response -//! async fn empty() {} -//! -//! // `StatusCode` gives an empty response with that status code -//! async fn empty_with_status() -> StatusCode { -//! StatusCode::NOT_FOUND -//! } -//! -//! // A tuple of `StatusCode` and something that implements `IntoResponse` can -//! // be used to override the status code -//! async fn with_status() -> (StatusCode, &'static str) { -//! (StatusCode::INTERNAL_SERVER_ERROR, "Something went wrong") -//! } -//! -//! // A tuple of `HeaderMap` and something that implements `IntoResponse` can -//! // be used to override the headers -//! async fn with_headers() -> (HeaderMap, &'static str) { -//! let mut headers = HeaderMap::new(); -//! headers.insert( -//! HeaderName::from_static("x-foo"), -//! HeaderValue::from_static("foo"), -//! ); -//! (headers, "foo") -//! } -//! -//! // You can also override both status and headers at the same time -//! async fn with_headers_and_status() -> (StatusCode, HeaderMap, &'static str) { -//! let mut headers = HeaderMap::new(); -//! headers.insert( -//! HeaderName::from_static("x-foo"), -//! HeaderValue::from_static("foo"), -//! ); -//! (StatusCode::INTERNAL_SERVER_ERROR, headers, "foo") -//! } -//! -//! // `Headers` makes building the header map easier and `impl Trait` is easier -//! // so you don't have to write the whole type -//! async fn with_easy_headers() -> impl IntoResponse { -//! Headers(vec![("x-foo", "foo")]) -//! } -//! -//! // `Html` gives a content-type of `text/html` -//! async fn html() -> Html<&'static str> { -//! Html("

Hello, World!

") -//! } -//! -//! // `Json` gives a content-type of `application/json` and works with any type -//! // that implements `serde::Serialize` -//! async fn json() -> Json { -//! Json(json!({ "data": 42 })) -//! } -//! -//! // `Result` where `T` and `E` implement `IntoResponse` is useful for -//! // returning errors -//! async fn result() -> Result<&'static str, StatusCode> { -//! Ok("all good") -//! } -//! -//! // `Response` gives full control -//! async fn response() -> Response { -//! Response::builder().body(Body::empty()).unwrap() -//! } -//! -//! let app = Router::new() -//! .route("/plain_text", get(plain_text)) -//! .route("/plain_text_string", get(plain_text_string)) -//! .route("/bytes", get(bytes)) -//! .route("/empty", get(empty)) -//! .route("/empty_with_status", get(empty_with_status)) -//! .route("/with_status", get(with_status)) -//! .route("/with_headers", get(with_headers)) -//! .route("/with_headers_and_status", get(with_headers_and_status)) -//! .route("/with_easy_headers", get(with_easy_headers)) -//! .route("/html", get(html)) -//! .route("/json", get(json)) -//! .route("/result", get(result)) -//! .route("/response", get(response)); -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; -//! ``` -//! -//! # Error handling -//! -//! In the context of axum an "error" specifically means if a [`Service`]'s -//! response future resolves to `Err(Service::Error)`. That means async handler -//! functions can _never_ fail since they always produce a response and their -//! `Service::Error` type is [`Infallible`]. Returning statuses like 404 or 500 -//! are _not_ errors. -//! -//! axum works this way because hyper will close the connection, without sending -//! a response, if an error is encountered. This is not desireable so axum makes -//! it impossible to forget to handle errors. -//! -//! Sometimes you need to route to fallible services or apply fallible -//! middleware in which case you need to handle the errors. That can be done -//! using things from [`error_handling`]. -//! -//! You can find examples here: -//! - [Routing to fallible services](#routing-to-fallible-services) -//! - [Applying fallible middleware](#applying-multiple-middleware) -//! -//! # Applying middleware -//! -//! axum is designed to take full advantage of the tower and tower-http -//! ecosystem of middleware. -//! -//! If you're new to tower we recommend you read its [guides][tower-guides] for -//! a general introduction to tower and its concepts. -//! -//! ## To individual handlers -//! -//! A middleware can be applied to a single handler like so: -//! -//! ```rust,no_run -//! use axum::{ -//! handler::Handler, -//! routing::get, -//! Router, -//! }; -//! use tower::limit::ConcurrencyLimitLayer; -//! -//! let app = Router::new() -//! .route( -//! "/", -//! get(handler.layer(ConcurrencyLimitLayer::new(100))), -//! ); -//! -//! async fn handler() {} -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; -//! ``` -//! -//! ## To groups of routes -//! -//! Middleware can also be applied to a group of routes like so: -//! -//! ```rust,no_run -//! use axum::{ -//! routing::{get, post}, -//! Router, -//! }; -//! use tower::limit::ConcurrencyLimitLayer; -//! -//! async fn handler() {} -//! -//! let app = Router::new() -//! .route("/", get(handler)) -//! .route("/foo", post(handler)) -//! .layer(ConcurrencyLimitLayer::new(100)); -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; -//! ``` -//! -//! Note that [`Router::layer`] applies the middleware to all previously added -//! routes, of that particular `Router`. If you need multiple groups of routes -//! with different middleware build them separately and combine them with -//! [`Router::merge`]: -//! -//! ```rust,no_run -//! use axum::{ -//! routing::{get, post}, -//! Router, -//! }; -//! use tower::limit::ConcurrencyLimitLayer; -//! # type MyAuthLayer = tower::layer::util::Identity; -//! -//! async fn handler() {} -//! -//! let foo = Router::new() -//! .route("/", get(handler)) -//! .route("/foo", post(handler)) -//! .layer(ConcurrencyLimitLayer::new(100)); -//! -//! let bar = Router::new() -//! .route("/requires-auth", get(handler)) -//! .layer(MyAuthLayer::new()); -//! -//! let app = foo.merge(bar); -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; -//! ``` -//! -//! ## Applying multiple middleware -//! -//! [`tower::ServiceBuilder`] can be used to combine multiple middleware: -//! -//! ```rust,no_run -//! use axum::{ -//! body::Body, -//! routing::get, -//! http::{Request, StatusCode}, -//! error_handling::HandleErrorLayer, -//! response::IntoResponse, -//! Router, BoxError, -//! }; -//! use tower::ServiceBuilder; -//! use tower_http::compression::CompressionLayer; -//! use std::{borrow::Cow, time::Duration}; -//! -//! let middleware_stack = ServiceBuilder::new() -//! // Handle errors from middleware -//! // -//! // This middleware most be added above any fallible -//! // ones if you're using `ServiceBuilder`, due to how ordering works -//! .layer(HandleErrorLayer::new(handle_error)) -//! // Return an error after 30 seconds -//! .timeout(Duration::from_secs(30)) -//! // Shed load if we're receiving too many requests -//! .load_shed() -//! // Process at most 100 requests concurrently -//! .concurrency_limit(100) -//! // Compress response bodies -//! .layer(CompressionLayer::new()); -//! -//! let app = Router::new() -//! .route("/", get(|_: Request| async { /* ... */ })) -//! .layer(middleware_stack); -//! -//! fn handle_error(error: BoxError) -> impl IntoResponse { -//! ( -//! StatusCode::INTERNAL_SERVER_ERROR, -//! format!("Something went wrong: {}", error), -//! ) -//! } -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; -//! ``` -//! -//! See [Error handling](#error-handling) for more details on general error handling in axum. -//! -//! ## Commonly used middleware -//! -//! [`tower::util`] and [`tower_http`] have a large collection of middleware that are compatible -//! with axum. Some commonly used are: -//! -//! ```rust,no_run -//! use axum::{ -//! body::{Body, BoxBody}, -//! routing::get, -//! http::{Request, Response}, -//! error_handling::HandleErrorLayer, -//! Router, -//! }; -//! use tower::{ -//! filter::AsyncFilterLayer, -//! util::AndThenLayer, -//! ServiceBuilder, -//! }; -//! use std::convert::Infallible; -//! use tower_http::trace::TraceLayer; -//! # -//! # fn handle_error(error: T) -> axum::http::StatusCode { -//! # axum::http::StatusCode::INTERNAL_SERVER_ERROR -//! # } -//! -//! let middleware_stack = ServiceBuilder::new() -//! // Handle errors from middleware -//! // -//! // This middleware most be added above any fallible -//! // ones if you're using `ServiceBuilder`, due to how ordering works -//! .layer(HandleErrorLayer::new(handle_error)) -//! // `TraceLayer` adds high level tracing and logging -//! .layer(TraceLayer::new_for_http()) -//! // `AsyncFilterLayer` lets you asynchronously transform the request -//! .layer(AsyncFilterLayer::new(map_request)) -//! // `AndThenLayer` lets you asynchronously transform the response -//! .layer(AndThenLayer::new(map_response)); -//! -//! async fn map_request(req: Request) -> Result, Infallible> { -//! Ok(req) -//! } -//! -//! async fn map_response(res: Response) -> Result, Infallible> { -//! Ok(res) -//! } -//! -//! let app = Router::new() -//! .route("/", get(|| async { /* ... */ })) -//! .layer(middleware_stack); -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; -//! ``` -//! -//! Additionally axum provides [`extract::extractor_middleware()`] for converting any extractor into -//! a middleware. Among other things, this can be useful for doing authorization. See -//! [`extract::extractor_middleware()`] for more details. -//! -//! See [Error handling](#error-handling) for more details on general error handling in axum. -//! -//! ## Writing your own middleware -//! -//! You can also write you own middleware by implementing [`tower::Service`]: -//! -//! ``` -//! use axum::{ -//! body::{Body, BoxBody}, -//! routing::get, -//! http::{Request, Response}, -//! Router, -//! }; -//! use futures::future::BoxFuture; -//! use tower::{Service, layer::layer_fn}; -//! use std::task::{Context, Poll}; -//! -//! #[derive(Clone)] -//! struct MyMiddleware { -//! inner: S, -//! } -//! -//! impl Service> for MyMiddleware -//! where -//! S: Service, Response = Response> + Clone + Send + 'static, -//! S::Future: Send + 'static, -//! ReqBody: Send + 'static, -//! ResBody: Send + 'static, -//! { -//! type Response = S::Response; -//! type Error = S::Error; -//! type Future = BoxFuture<'static, Result>; -//! -//! fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { -//! self.inner.poll_ready(cx) -//! } -//! -//! fn call(&mut self, mut req: Request) -> Self::Future { -//! println!("`MyMiddleware` called!"); -//! -//! // best practice is to clone the inner service like this -//! // see https://github.com/tower-rs/tower/issues/547 for details -//! let clone = self.inner.clone(); -//! let mut inner = std::mem::replace(&mut self.inner, clone); -//! -//! Box::pin(async move { -//! let res: Response = inner.call(req).await?; -//! -//! println!("`MyMiddleware` received the response"); -//! -//! Ok(res) -//! }) -//! } -//! } -//! -//! let app = Router::new() -//! .route("/", get(|| async { /* ... */ })) -//! .layer(layer_fn(|inner| MyMiddleware { inner })); -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; -//! ``` -//! -//! # Sharing state with handlers -//! -//! It is common to share some state between handlers for example to share a -//! pool of database connections or clients to other services. That can be done -//! using the [`AddExtension`] middleware (applied with [`AddExtensionLayer`]) -//! and the [`extract::Extension`] extractor: -//! -//! ```rust,no_run -//! use axum::{ -//! AddExtensionLayer, -//! extract, -//! routing::get, -//! Router, -//! }; -//! use std::sync::Arc; -//! -//! struct State { -//! // ... -//! } -//! -//! let shared_state = Arc::new(State { /* ... */ }); -//! -//! let app = Router::new() -//! .route("/", get(handler)) -//! .layer(AddExtensionLayer::new(shared_state)); -//! -//! async fn handler( -//! state: extract::Extension>, -//! ) { -//! let state: Arc = state.0; -//! -//! // ... -//! } -//! # async { -//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -//! # }; -//! ``` -//! -//! # Required dependencies -//! -//! To use axum there are a few dependencies you have pull in as well: -//! -//! ```toml -//! [dependencies] -//! axum = "" -//! hyper = { version = "", features = ["full"] } -//! tokio = { version = "", features = ["full"] } -//! tower = "" -//! ``` -//! -//! The `"full"` feature for hyper and tokio isn't strictly necessary but its -//! the easiest way to get started. -//! -//! Note that [`hyper::Server`] is re-exported by axum so if thats all you need -//! then you don't have to explicitly depend on hyper. -//! -//! Tower isn't strictly necessary either but helpful for testing. See the -//! testing example in the repo to learn more about testing axum apps. -//! -//! # Examples -//! -//! The axum repo contains [a number of examples][examples] that show how to put all the -//! pieces together. -//! -//! # Feature flags -//! -//! axum uses a set of [feature flags] to reduce the amount of compiled and -//! optional dependencies. -//! -//! The following optional features are available: -//! -//! - `headers`: Enables extracting typed headers via [`extract::TypedHeader`]. -//! - `http1`: Enables hyper's `http1` feature. Enabled by default. -//! - `http2`: Enables hyper's `http2` feature. -//! - `json`: Enables the [`Json`] type and some similar convenience functionality. -//! Enabled by default. -//! - `multipart`: Enables parsing `multipart/form-data` requests with [`extract::Multipart`]. -//! - `tower-log`: Enables `tower`'s `log` feature. Enabled by default. -//! - `ws`: Enables WebSockets support via [`extract::ws`]. +#![doc = include_str!("docs/table_of_contents.md")] +#![doc = include_str!("docs/high_level_features.md")] +#![doc = include_str!("docs/compatibility.md")] +#![doc = include_str!("docs/example.md")] +#![doc = include_str!("docs/handlers.md")] +#![doc = include_str!("docs/routing.md")] +#![doc = include_str!("docs/extractors.md")] +#![doc = include_str!("docs/building_responses.md")] +#![doc = include_str!("docs/error_handling.md")] +#![doc = include_str!("docs/applying_middleware.md")] +#![doc = include_str!("docs/sharing_state_with_handlers.md")] +#![doc = include_str!("docs/required_dependencies.md")] +#![doc = include_str!("docs/examples.md")] +#![doc = include_str!("docs/feature_flags.md")] //! //! [`tower`]: https://crates.io/crates/tower //! [`tower-http`]: https://crates.io/crates/tower-http