Rework docs (#437)

This reworks axum's docs in an attempt to make things easier to find. Previously I wasn't a fan of those docs for the same topic were spread across the root module docs and more specific places like types and methods.

This changes it such that the root module docs only gives a high level introduction to a topic, perhaps with a small example, and then link to other places where all the details are. This means `Router` is now the single place to learn about routing, and etc for the topics like handlers and error handling.
This commit is contained in:
David Pedersen 2021-11-01 22:13:37 +01:00 committed by GitHub
parent 1c6038c09f
commit 9465128f3e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
46 changed files with 1978 additions and 1836 deletions

View file

@ -54,12 +54,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#405])
- **breaking:** Method routing for services have been moved from `axum::service`
to `axum::routing`. So `axum::service::get` now lives at, etc.
`axum::routing::service_method_router::get`, etc. ([#405])
`axum::routing::service_method_routing::get`, etc. ([#405])
- **breaking:** `Router::or` renamed to `Router::merge` and will now panic on
overlapping routes. It now only accepts `Router`s and not general `Service`s.
Use `Router::fallback` for adding fallback routes ([#408])
- **added:** `Router::fallback` for adding handlers for request that didn't
match any routes ([#408])
match any routes. `Router::fallback` must be use instead of `nest("/", _)` ([#408])
- **breaking:** `EmptyRouter` has been renamed to `MethodNotAllowed` as its only
used in method routers and not in path routers (`Router`)
- **breaking:** Remove support for routing based on the `CONNECT` method. An
@ -119,7 +119,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
use axum::{
Router, service,
body::Body,
routing::service_method_router::get,
routing::service_method_routing::get,
response::IntoResponse,
http::{Request, Response},
error_handling::HandleErrorExt, // for `.handle_error`

View file

@ -62,6 +62,7 @@ tokio = { version = "1.6.1", features = ["macros", "rt", "rt-multi-thread", "net
tokio-stream = "0.1"
tracing = "0.1"
uuid = { version = "0.8", features = ["serde", "v4"] }
anyhow = "1.0"
[dev-dependencies.tower]
package = "tower"

View file

@ -25,9 +25,13 @@ applications written using [`hyper`] or [`tonic`].
## Usage example
Note this example uses `main` which contains breaking changes. See the
[v0.2.x](https://github.com/tokio-rs/axum/tree/v0.2.x) branch for an example
using 0.2.
```rust
use axum::{
handler::{get, post},
routing::{get, post},
http::StatusCode,
response::IntoResponse,
Json, Router,

View file

@ -4,23 +4,58 @@
//! cargo run -p example-hello-world
//! ```
use axum::{response::Html, routing::get, Router};
use std::net::SocketAddr;
#[tokio::main]
async fn main() {
// build our application with a route
let app = Router::new().route("/", get(handler));
use axum::async_trait;
use axum::http::StatusCode;
use axum::{
extract::{extractor_middleware, FromRequest, RequestParts},
routing::{get, post},
Router,
};
// run it
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
println!("listening on {}", addr);
axum::Server::bind(&addr)
// An extractor that performs authorization.
struct RequireAuth;
#[async_trait]
impl<B> FromRequest<B> for RequireAuth
where
B: Send,
{
type Rejection = StatusCode;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
let auth_header = req
.headers()
.and_then(|headers| headers.get(axum::http::header::AUTHORIZATION))
.and_then(|value| value.to_str().ok());
if let Some(value) = auth_header {
if value == "secret" {
return Ok(Self);
}
}
Err(StatusCode::UNAUTHORIZED)
}
}
async fn handler() {
// If we get here the request has been authorized
}
async fn other_handler() {
// If we get here the request has been authorized
}
let app = Router::new()
.route("/", get(handler))
.route("/foo", post(other_handler))
// The extractor will run before all routes
.layer(extractor_middleware::<RequireAuth>());
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
async fn handler() -> Html<&'static str> {
Html("<h1>Hello, World!</h1>")
}

View file

@ -9,7 +9,7 @@ use axum::{
extract::TypedHeader,
http::StatusCode,
response::sse::{Event, Sse},
routing::{get, service_method_router as service},
routing::{get, service_method_routing as service},
Router,
};
use futures::stream::{self, Stream};

View file

@ -5,7 +5,7 @@
//! ```
use axum::{
error_handling::HandleErrorExt, http::StatusCode, routing::service_method_router as service,
error_handling::HandleErrorExt, http::StatusCode, routing::service_method_routing as service,
Router,
};
use std::net::SocketAddr;

View file

@ -14,7 +14,7 @@ use axum::{
},
http::StatusCode,
response::IntoResponse,
routing::{get, service_method_router as service},
routing::{get, service_method_routing as service},
Router,
};
use std::net::SocketAddr;

View file

@ -1,253 +0,0 @@
# 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<Body>| 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<T>(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<Body>) -> Result<Request<Body>, Infallible> {
Ok(req)
}
async fn map_response(res: Response<BoxBody>) -> Result<Response<BoxBody>, 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<S> {
inner: S,
}
impl<S, ReqBody, ResBody> Service<Request<ReqBody>> for MyMiddleware<S>
where
S: Service<Request<ReqBody>, Response = Response<ResBody>> + 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<Self::Response, Self::Error>>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}
fn call(&mut self, mut req: Request<ReqBody>) -> 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<ResBody> = 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();
# };
```

View file

@ -1,4 +0,0 @@
# 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.

View file

@ -1,19 +1,130 @@
# Error handling
Error handling model and utilities
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's error handling model
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.
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.
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`].
axum does this by requiring all services have [`Infallible`] as their error
type. `Infallible` is the error type for errors that can never happen.
You can find examples here:
- [Routing to fallible services](#routing-to-fallible-services)
- [Applying fallible middleware](#applying-multiple-middleware)
This means if you define a handler like:
```rust
use axum::http::StatusCode;
async fn handler() -> Result<String, StatusCode> {
# 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.
# 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.
You can handle errors from services using [`HandleErrorExt::handle_error`]:
```rust
use axum::{
Router,
body::Body,
http::{Request, Response, StatusCode},
error_handling::HandleErrorExt, // for `.handle_error()`
};
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(
"/",
// 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`.
some_fallible_service.handle_error(handle_anyhow_error),
);
// handle errors by converting them into something that implements
// `IntoResponse`
fn handle_anyhow_error(err: anyhow::Error) -> (StatusCode, String) {
(
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {}", err),
)
}
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
```
# 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))
);
fn handle_timeout_error(err: BoxError) -> (StatusCode, String) {
if err.is::<tower::timeout::error::Elapsed>() {
(
StatusCode::REQUEST_TIMEOUT,
"Request took too long".to_string(),
)
} else {
(
StatusCode::INTERNAL_SERVER_ERROR,
format!("Unhandled internal error: {}", err),
)
}
}
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
```
[`tower::Service`]: `tower::Service`
[`Infallible`]: std::convert::Infallible
[`Response<_>`]: http::Response
[`IntoResponse`]: crate::response::IntoResponse

View file

@ -1,22 +0,0 @@
# 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();
}
```

View file

@ -1,4 +0,0 @@
# Examples
The axum repo contains [a number of examples][examples] that show how to put all the
pieces together.

476
src/docs/extract.md Normal file
View file

@ -0,0 +1,476 @@
Types and traits for extracting data from requests.
A handler function is an async function that takes any number of
"extractors" as arguments. An extractor is a type that implements
[`FromRequest`](crate::extract::FromRequest).
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<CreateUser>) {
// ...
}
let app = Router::new().route("/users", post(create_user));
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
```
# 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<u32>) {}
// `Query` gives you the query parameters and deserializes them.
async fn query(Query(params): Query<HashMap<String, String>>) {}
// `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` feature
async fn user_agent(TypedHeader(user_agent): TypedHeader<UserAgent>) {}
// `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<Value>) {}
// `Request` gives you the whole request for maximum control
async fn request(request: Request<Body>) {}
// `Extension` extracts data from "request extensions"
// This is commonly used to share state with handlers
async fn extension(Extension(state): Extension<State>) {}
#[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::{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,
}
impl Default for Pagination {
fn default() -> Self {
Self { page: 1, per_page: 30 }
}
}
async fn get_user_things(
Path(user_id): Path<Uuid>,
pagination: Option<Query<Pagination>>,
) {
let Query(pagination) = pagination.unwrap_or_default();
// ...
}
# 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<UserAgent>,
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<Body>) {
// ...
}
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<Json<Value>>) {
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<Json<Value>, 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<T, T::Rejection>` 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.
# 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`] thats contained in
[`JsonRejection::InvalidJsonBody`]. 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<Json<Value>, JsonRejection>) -> impl IntoResponse {
match result {
// if the client sent valid JSON then we're good
Ok(Json(payload)) => Ok(Json(json!({ "payload": payload }))),
Err(err) => match err {
// attempt to extract the inner `serde_json::Error`, if that
// succeeds we can provide a more specific error
JsonRejection::InvalidJsonBody(err) => {
if let Some(serde_json_err) = find_error_source::<serde_json::Error>(&err) {
Err((
StatusCode::BAD_REQUEST,
format!(
"Invalid JSON at line {} column {}",
serde_json_err.line(),
serde_json_err.column()
),
))
} else {
Err((StatusCode::BAD_REQUEST, "Unknown error".to_string()))
}
}
// handle other rejections from the `Json` extractor
JsonRejection::MissingJsonContentType(_) => Err((
StatusCode::BAD_REQUEST,
"Missing `Content-Type: application/json` header".to_string(),
)),
JsonRejection::BodyAlreadyExtracted(_) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
"Body already extracted".to_string(),
)),
JsonRejection::HeadersAlreadyExtracted(_) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
"Headers already extracted".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 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::<T>() {
Some(err)
} else if let Some(source) = err.source() {
find_error_source(source)
} else {
None
}
}
```
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 [`FromRequest`]:
```rust,no_run
use axum::{
async_trait,
extract::{FromRequest, RequestParts},
routing::get,
Router,
};
use http::{StatusCode, header::{HeaderValue, USER_AGENT}};
struct ExtractUserAgent(HeaderValue);
#[async_trait]
impl<B> FromRequest<B> for ExtractUserAgent
where
B: Send,
{
type Rejection = (StatusCode, &'static str);
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
let user_agent = req.headers().and_then(|headers| headers.get(USER_AGENT));
if let Some(user_agent) = 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));
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
```
# Request body extractors
Most of the time your request body type will be [`body::Body`] (a re-export
of [`hyper::Body`]), which is directly supported by all extractors.
However if you're applying a tower middleware that changes the request body type
you might have to apply a different body type to some extractors:
```rust
use std::{
task::{Context, Poll},
pin::Pin,
};
use tower_http::map_request_body::MapRequestBodyLayer;
use axum::{
extract::{self, BodyStream},
body::Body,
routing::get,
http::{header::HeaderMap, Request},
Router,
};
struct MyBody<B>(B);
impl<B> http_body::Body for MyBody<B>
where
B: http_body::Body + Unpin,
{
type Data = B::Data;
type Error = B::Error;
fn poll_data(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
Pin::new(&mut self.0).poll_data(cx)
}
fn poll_trailers(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
Pin::new(&mut self.0).poll_trailers(cx)
}
}
let app = Router::new()
.route(
"/string",
// `String` works directly with any body type
get(|_: String| async {})
)
.route(
"/body",
// `extract::Body` defaults to `axum::body::Body`
// but can be customized
get(|_: extract::RawBody<MyBody<Body>>| async {})
)
.route(
"/body-stream",
// same for `extract::BodyStream`
get(|_: extract::BodyStream| async {}),
)
.route(
// and `Request<_>`
"/request",
get(|_: Request<MyBody<Body>>| async {})
)
// middleware that changes the request body type
.layer(MapRequestBodyLayer::new(MyBody));
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
```
[`body::Body`]: crate::body::Body
[customize-extractor-error]: https://github.com/tokio-rs/axum/blob/main/examples/customize-extractor-error/src/main.rs

View file

@ -1,270 +0,0 @@
# 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<CreateUser>) {
// ...
}
# 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<u32>) {}
// `Query` gives you the query parameters and deserializes them.
async fn query(Query(params): Query<HashMap<String, String>>) {}
// `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<UserAgent>) {}
// `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<Value>) {}
// `Request` gives you the whole request for maximum control
async fn request(request: Request<Body>) {}
// `Extension` extracts data from "request extensions"
// See the "Sharing state with handlers" section for more details
async fn extension(Extension(state): Extension<State>) {}
#[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<Uuid>,
pagination: Option<extract::Query<Pagination>>,
) {
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<UserAgent>,
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<Body>) {
// ...
}
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<Json<Value>>) {
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<Json<Value>, 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<T, T::Rejection>` 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.

View file

@ -1,15 +0,0 @@
# 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`].

View file

@ -1,68 +0,0 @@
# 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<String, StatusCode> {
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<B, T>,
| ------------- 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.

View file

@ -0,0 +1,8 @@
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](crate::response).
Handlers is where your application logic lives and axum applications are built
by routing between handlers.
[`debug_handler`]: https://docs.rs/axum-debug/latest/axum_debug/attr.debug_handler.html

View file

@ -1,14 +0,0 @@
# 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`].

166
src/docs/middleware.md Normal file
View file

@ -0,0 +1,166 @@
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.
axum supports adding middleware to both individual handlers and entire routers.
For more details on that see
- [Individual handlers](crate::handler::Handler::layer)
- [Routers](crate::routing::Router::layer)
## Applying multiple middleware
Its recommended to use [`tower::ServiceBuilder`] to apply multiple middleware at
once, instead of calling [`Router::layer`] repeatedly:
```rust
use axum::{
routing::get,
AddExtensionLayer,
Router,
};
use tower_http::{trace::TraceLayer};
use tower::{ServiceBuilder, limit::ConcurrencyLimitLayer};
async fn handler() {}
#[derive(Clone)]
struct State {}
let app = Router::new()
.route("/", get(handler))
.layer(
ServiceBuilder::new()
.layer(TraceLayer::new_for_http())
.layer(ConcurrencyLimitLayer::new(64))
.layer(AddExtensionLayer::new(State {}))
);
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
```
## Middleware and errors
If you're applying middleware that produces errors you have to handle the errors
so they're converted into responses. You can learn more about doing that
[here](crate::error_handling).
## Commonly used middleware
[`tower`] and [`tower_http`] have a large collection of middleware that are
compatible with axum. Some commonly used middleware 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<T>(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<Body>) -> Result<Request<Body>, Infallible> {
Ok(req)
}
async fn map_response(res: Response<BoxBody>) -> Result<Response<BoxBody>, 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. See [`extract::extractor_middleware()`] for
more details.
## Writing your own middleware
You can also write you own middleware by implementing [`tower::Service`]:
```rust
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<S> {
inner: S,
}
impl<S> Service<Request<Body>> for MyMiddleware<S>
where
S: Service<Request<Body>, Response = Response<BoxBody>> + Clone + Send + 'static,
S::Future: Send + 'static,
{
type Response = S::Response;
type Error = S::Error;
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}
fn call(&mut self, mut req: Request<Body>) -> 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<BoxBody> = 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();
# };
```

View file

@ -1,20 +0,0 @@
# Required dependencies
To use axum there are a few dependencies you have pull in as well:
```toml
[dependencies]
axum = "<latest-version>"
hyper = { version = "<latest-version>", features = ["full"] }
tokio = { version = "<latest-version>", features = ["full"] }
tower = "<latest-version>"
```
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.

View file

@ -1,7 +1,8 @@
Types and traits for generating responses.
# Building responses
Anything that implements [`IntoResponse`](response::IntoResponse) can be
returned from a handler:
Anything that implements [`IntoResponse`] can be returned from a handler:
```rust,no_run
use axum::{

View file

@ -1,286 +0,0 @@
# 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<String>| 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<Body>| async { /* ... */ }))
}
let app = Router::new()
.route("/", get(|_: Request<Body>| async { /* ... */ }))
.nest("/api", api_routes());
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
```
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<Body>| 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<Body>| async move {
let body = Body::from(format!("Hi from `{} /foo`", req.method()));
let body = axum::body::box_body(body);
let res = Response::new(body);
Ok::<_, 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<Body>| 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<Body>| 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.

View file

@ -0,0 +1,70 @@
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, Uri},
};
let app = Router::new()
.route("/foo", get(|| async { /* ... */ }))
.fallback(fallback.into_service());
async fn fallback(uri: Uri) -> impl IntoResponse {
(StatusCode::NOT_FOUND, format!("No route for {}", uri))
}
# async {
# hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
```
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.
## When used with `Router::merge`
If a router with a fallback is merged with another router that also has
a fallback the fallback of the second router takes precedence:
```rust
use axum::{
Router,
routing::get,
handler::Handler,
response::IntoResponse,
http::{StatusCode, Uri},
};
let one = Router::new()
.route("/one", get(|| async {}))
.fallback(fallback_one.into_service());
let two = Router::new()
.route("/two", get(|| async {}))
.fallback(fallback_two.into_service());
let app = one.merge(two);
async fn fallback_one() -> impl IntoResponse {}
async fn fallback_two() -> impl IntoResponse {}
// the fallback for `app` is `fallback_two`
# async {
# hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
```
If only one of the routers have a fallback that will be used in the
merged router.
## When used with `Router::nest`
If a router with a fallback is nested inside another router the fallback
of the nested router will be discarded and not used. This is such that
the outer router's fallback takes precedence.

View file

@ -0,0 +1,80 @@
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<SocketAddr>) -> String {
format!("Hello {}", addr)
}
# async {
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(
app.into_make_service_with_connect_info::<SocketAddr, _>()
)
.await
.expect("server failed");
# };
```
You can implement custom a [`Connected`] like so:
```rust
use axum::{
extract::connect_info::{ConnectInfo, Connected},
routing::get,
Router,
};
use hyper::server::conn::AddrStream;
let app = Router::new().route("/", get(handler));
async fn handler(
ConnectInfo(my_connect_info): ConnectInfo<MyConnectInfo>,
) -> String {
format!("Hello {:?}", my_connect_info)
}
#[derive(Clone, Debug)]
struct MyConnectInfo {
// ...
}
impl Connected<&AddrStream> for MyConnectInfo {
fn connect_info(target: &AddrStream) -> Self {
MyConnectInfo {
// ...
}
}
}
# async {
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(
app.into_make_service_with_connect_info::<MyConnectInfo, _>()
)
.await
.expect("server failed");
# };
```
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.rs

136
src/docs/routing/layer.md Normal file
View file

@ -0,0 +1,136 @@
Apply a [`tower::Layer`] to the router.
All requests to the router will be processed by the layer's
corresponding middleware.
This can be used to add additional processing to a request for a group
of routes.
Note this differs from [`handler::Layered`](crate::handler::Layered)
which adds a middleware to a single handler.
# Example
Adding the [`tower::limit::ConcurrencyLimit`] middleware to a group of
routes can be done like so:
```rust
use axum::{
routing::get,
Router,
};
use tower::limit::{ConcurrencyLimitLayer, ConcurrencyLimit};
async fn first_handler() {}
async fn second_handler() {}
async fn third_handler() {}
// All requests to `handler` and `other_handler` will be sent through
// `ConcurrencyLimit`
let app = Router::new().route("/", get(first_handler))
.route("/foo", get(second_handler))
.layer(ConcurrencyLimitLayer::new(64))
// Request to `GET /bar` will go directly to `third_handler` and
// wont be sent through `ConcurrencyLimit`
.route("/bar", get(third_handler));
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
```
This is commonly used to add middleware such as tracing/logging to your
entire app:
```rust
use axum::{
routing::get,
Router,
};
use tower_http::trace::TraceLayer;
async fn first_handler() {}
async fn second_handler() {}
async fn third_handler() {}
let app = Router::new()
.route("/", get(first_handler))
.route("/foo", get(second_handler))
.route("/bar", get(third_handler))
.layer(TraceLayer::new_for_http());
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
```
# Applying multiple middleware
Its recommended to use [`tower::ServiceBuilder`] to apply multiple middleware at
once, instead of calling `layer` repeatedly:
```rust
use axum::{
routing::get,
AddExtensionLayer,
Router,
};
use tower_http::{trace::TraceLayer};
use tower::{ServiceBuilder, limit::ConcurrencyLimitLayer};
async fn handler() {}
#[derive(Clone)]
struct State {}
let app = Router::new()
.route("/", get(handler))
.layer(
ServiceBuilder::new()
.layer(TraceLayer::new_for_http())
.layer(ConcurrencyLimitLayer::new(64))
.layer(AddExtensionLayer::new(State {}))
);
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
```
# Error handling
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| StatusCode::REQUEST_TIMEOUT))
.layer(TimeoutLayer::new(Duration::from_secs(10)))
);
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
```
See [`error_handling`](crate::error_handling) for more details on axum's error
handling model.

38
src/docs/routing/merge.md Normal file
View file

@ -0,0 +1,38 @@
Merge two routers into one.
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
// - POST /teams
# async {
# hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
```

125
src/docs/routing/nest.md Normal file
View file

@ -0,0 +1,125 @@
Nest a group of routes (or a [`Service`]) at some path.
This allows you to break your application into smaller pieces and compose
them together.
# 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
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
```
# How the URI changes
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.
# 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<HashMap<String, String>>) {
// Both `version` and `id` were captured even though `users_api` only
// explicitly captures `id`.
let version = params.get("version");
let id = params.get("id");
}
let users_api = Router::new().route("/users/:id", get(users_get));
let app = Router::new().nest("/:version/api", users_api);
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
```
# Nesting services
`nest` also accepts any [`Service`]. This can for example be used with
[`tower_http::services::ServeDir`] to serve static files from a directory:
```rust
use axum::{
Router,
routing::service_method_routing::get,
error_handling::HandleErrorExt,
http::StatusCode,
};
use std::{io, convert::Infallible};
use tower_http::services::ServeDir;
// Serves files inside the `public` directory at `GET /public/*`
let serve_dir_service = ServeDir::new("public")
.handle_error(|error: io::Error| {
(
StatusCode::INTERNAL_SERVER_ERROR,
format!("Unhandled internal error: {}", error),
)
});
let app = Router::new().nest("/public", get(serve_dir_service));
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
```
# Differences to 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 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();
# };
```
# 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

196
src/docs/routing/route.md Normal file
View file

@ -0,0 +1,196 @@
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.
`service` is the [`Service`] that should receive the request if the path matches
`path`. `service` will commonly be a handler wrapped in a method router like
[`get`](crate::routing::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`.
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`](crate::extract::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`
Wildcard captures can also be extracted using [`Path`](crate::extract::Path).
# 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<u64>) {}
async fn do_users_action(Path((version, id)): Path<(String, u64)>) {}
async fn serve_asset(Path(path): Path<String>) {}
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
```
# Routing to any [`Service`]
axum also supports routing to general [`Service`]s:
```rust,no_run
use axum::{
Router,
body::Body,
routing::service_method_routing 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<Body>| 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<Body>| async move {
let body = Body::from(format!("Hi from `{} /foo`", req.method()));
let body = axum::body::box_body(body);
let res = Response::new(body);
Ok::<_, 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_method_routing`] module for more
details.
# 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 {}));
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
```
The static route `/foo` and the dynamic route `/:key` are not considered to
overlap and `/foo` will take precedence.
Take care when using [`Router::nest`] as it behaves like a wildcard route.
Therefore this setup panics:
```rust,should_panic
use axum::{routing::get, Router};
let app = Router::new()
// this is similar to `/api/*`
.nest("/api", get(|| async {}))
// which overlaps with this route
.route("/api/users", get(|| async {}));
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
```
Also panics if `path` is empty.
## Nesting
`route` cannot be used to nest `Router`s. Instead use [`Router::nest`].
Attempting to will result in a panic:
```rust,should_panic
use axum::{routing::get, Router};
let app = Router::new().route(
"/",
Router::new().route("/foo", get(|| async {})),
);
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
```

View file

@ -1,37 +0,0 @@
# 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<Arc<State>>,
) {
let state: Arc<State> = state.0;
// ...
}
# async {
# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
```

View file

@ -1,29 +0,0 @@
# 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)

View file

@ -1,33 +1,22 @@
//! Error handling utilities
//!
//! See [error handling](../index.html#error-handling) for more details on how
//! error handling works in axum.
#![doc = include_str!("../docs/error_handling.md")]
use crate::{
body::{box_body, BoxBody},
response::IntoResponse,
BoxError,
};
use crate::{body::BoxBody, response::IntoResponse, BoxError};
use bytes::Bytes;
use futures_util::ready;
use http::{Request, Response};
use pin_project_lite::pin_project;
use std::convert::Infallible;
use std::{
fmt,
future::Future,
marker::PhantomData,
pin::Pin,
task::{Context, Poll},
};
use tower::{util::Oneshot, ServiceExt as _};
use tower_layer::Layer;
use tower_service::Service;
/// [`Layer`] that applies [`HandleErrorLayer`] which is a [`Service`] adapter
/// [`Layer`] that applies [`HandleError`] which is a [`Service`] adapter
/// that handles errors by converting them into responses.
///
/// See [error handling](../index.html#error-handling) for more details.
/// See [module docs](self) for more details on axum's error handling model.
pub struct HandleErrorLayer<F, B> {
f: F,
_marker: PhantomData<fn() -> B>,
@ -80,7 +69,7 @@ impl<F, B> fmt::Debug for HandleErrorLayer<F, B> {
/// A [`Service`] adapter that handles errors by converting them into responses.
///
/// See [error handling](../index.html#error-handling) for more details.
/// See [module docs](self) for more details on axum's error handling model.
pub struct HandleError<S, F, B> {
inner: S,
f: F,
@ -130,7 +119,7 @@ where
{
type Response = Response<BoxBody>;
type Error = Infallible;
type Future = HandleErrorFuture<Oneshot<S, Request<ReqBody>>, F>;
type Future = future::HandleErrorFuture<Oneshot<S, Request<ReqBody>>, F>;
#[inline]
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
@ -138,16 +127,17 @@ where
}
fn call(&mut self, req: Request<ReqBody>) -> Self::Future {
HandleErrorFuture {
future::HandleErrorFuture {
f: Some(self.f.clone()),
inner: self.inner.clone().oneshot(req),
}
}
}
/// Handle errors this service might produce, by mapping them to responses.
/// Extension trait to [`Service`] for handling errors by mapping them to
/// responses.
///
/// See [error handling](../index.html#error-handling) for more details.
/// See [module docs](self) for more details on axum's error handling model.
pub trait HandleErrorExt<B>: Service<Request<B>> + Sized {
/// Apply a [`HandleError`] middleware.
fn handle_error<F>(self, f: F) -> HandleError<Self, F, B> {
@ -157,35 +147,55 @@ pub trait HandleErrorExt<B>: Service<Request<B>> + Sized {
impl<B, S> HandleErrorExt<B> for S where S: Service<Request<B>> {}
pin_project! {
/// Response future for [`HandleError`](super::HandleError).
#[derive(Debug)]
pub struct HandleErrorFuture<Fut, F> {
#[pin]
pub(super) inner: Fut,
pub(super) f: Option<F>,
pub mod future {
//! Future types.
use crate::{
body::{box_body, BoxBody},
response::IntoResponse,
BoxError,
};
use bytes::Bytes;
use futures_util::ready;
use http::Response;
use pin_project_lite::pin_project;
use std::convert::Infallible;
use std::{
future::Future,
pin::Pin,
task::{Context, Poll},
};
pin_project! {
/// Response future for [`HandleError`](super::HandleError).
#[derive(Debug)]
pub struct HandleErrorFuture<Fut, F> {
#[pin]
pub(super) inner: Fut,
pub(super) f: Option<F>,
}
}
}
impl<Fut, F, E, B, Res> Future for HandleErrorFuture<Fut, F>
where
Fut: Future<Output = Result<Response<B>, E>>,
F: FnOnce(E) -> Res,
Res: IntoResponse,
B: http_body::Body<Data = Bytes> + Send + Sync + 'static,
B::Error: Into<BoxError> + Send + Sync + 'static,
{
type Output = Result<Response<BoxBody>, Infallible>;
impl<Fut, F, E, B, Res> Future for HandleErrorFuture<Fut, F>
where
Fut: Future<Output = Result<Response<B>, E>>,
F: FnOnce(E) -> Res,
Res: IntoResponse,
B: http_body::Body<Data = Bytes> + Send + Sync + 'static,
B::Error: Into<BoxError> + Send + Sync + 'static,
{
type Output = Result<Response<BoxBody>, Infallible>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
match ready!(this.inner.poll(cx)) {
Ok(res) => Ok(res.map(box_body)).into(),
Err(err) => {
let f = this.f.take().unwrap();
let res = f(err);
Ok(res.into_response().map(box_body)).into()
match ready!(this.inner.poll(cx)) {
Ok(res) => Ok(res.map(box_body)).into(),
Err(err) => {
let f = this.f.take().unwrap();
let res = f(err);
Ok(res.into_response().map(box_body)).into()
}
}
}
}

View file

@ -5,7 +5,7 @@
//! [`Router::into_make_service_with_connect_info`]: crate::routing::Router::into_make_service_with_connect_info
use super::{Extension, FromRequest, RequestParts};
use crate::{AddExtension, AddExtensionLayer};
use crate::{AddExtension, AddExtensionLayer, Router};
use async_trait::async_trait;
use hyper::server::conn::AddrStream;
use std::future::ready;
@ -25,8 +25,8 @@ use tower_service::Service;
///
/// [`MakeService`]: tower::make::MakeService
/// [`Router::into_make_service_with_connect_info`]: crate::routing::Router::into_make_service_with_connect_info
pub struct IntoMakeServiceWithConnectInfo<S, C> {
svc: S,
pub struct IntoMakeServiceWithConnectInfo<B, C> {
router: Router<B>,
_connect_info: PhantomData<fn() -> C>,
}
@ -34,25 +34,21 @@ pub struct IntoMakeServiceWithConnectInfo<S, C> {
fn traits() {
use crate::tests::*;
assert_send::<IntoMakeServiceWithConnectInfo<(), NotSendSync>>();
assert_sync::<IntoMakeServiceWithConnectInfo<(), NotSendSync>>();
}
impl<S, C> IntoMakeServiceWithConnectInfo<S, C> {
pub(crate) fn new(svc: S) -> Self {
impl<B, C> IntoMakeServiceWithConnectInfo<B, C> {
pub(crate) fn new(router: Router<B>) -> Self {
Self {
svc,
router,
_connect_info: PhantomData,
}
}
}
impl<S, C> fmt::Debug for IntoMakeServiceWithConnectInfo<S, C>
where
S: fmt::Debug,
{
impl<B, C> fmt::Debug for IntoMakeServiceWithConnectInfo<B, C> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("IntoMakeServiceWithConnectInfo")
.field("svc", &self.svc)
.field("router", &self.router)
.finish()
}
}
@ -77,14 +73,13 @@ impl Connected<&AddrStream> for SocketAddr {
}
}
impl<S, C, T> Service<T> for IntoMakeServiceWithConnectInfo<S, C>
impl<B, C, T> Service<T> for IntoMakeServiceWithConnectInfo<B, C>
where
S: Clone,
C: Connected<T>,
{
type Response = AddExtension<S, ConnectInfo<C>>;
type Response = AddExtension<Router<B>, ConnectInfo<C>>;
type Error = Infallible;
type Future = ResponseFuture<Self::Response>;
type Future = ResponseFuture<B, C>;
#[inline]
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
@ -93,15 +88,15 @@ where
fn call(&mut self, target: T) -> Self::Future {
let connect_info = ConnectInfo(C::connect_info(target));
let svc = AddExtensionLayer::new(connect_info).layer(self.svc.clone());
let svc = AddExtensionLayer::new(connect_info).layer(self.router.clone());
ResponseFuture::new(ready(Ok(svc)))
}
}
opaque_future! {
/// Response future for [`IntoMakeServiceWithConnectInfo`].
pub type ResponseFuture<T> =
std::future::Ready<Result<T, Infallible>>;
pub type ResponseFuture<B, C> =
std::future::Ready<Result<AddExtension<Router<B>, ConnectInfo<C>>, Infallible>>;
}
/// Extractor for getting connection information produced by a [`Connected`].

View file

@ -37,52 +37,38 @@ use tower_service::Service;
///
/// ```rust
/// use axum::{
/// extract::{extractor_middleware, FromRequest, RequestParts},
/// routing::{get, post},
/// Router,
/// async_trait,
/// extract::{extractor_middleware, FromRequest, RequestParts},
/// http::StatusCode,
/// routing::{get, post},
/// };
/// use http::StatusCode;
/// use async_trait::async_trait;
/// use std::convert::Infallible;
///
/// // An extractor that performs authorization.
/// struct RequireAuth;
/// struct MyExtractor;
///
/// #[async_trait]
/// impl<B> FromRequest<B> for RequireAuth
/// impl<B> FromRequest<B> for MyExtractor
/// where
/// B: Send,
/// {
/// type Rejection = StatusCode;
/// type Rejection = Infallible;
///
/// async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
/// let auth_header = req
/// .headers()
/// .and_then(|headers| headers.get(http::header::AUTHORIZATION))
/// .and_then(|value| value.to_str().ok());
///
/// if let Some(value) = auth_header {
/// if value == "secret" {
/// return Ok(Self);
/// }
/// }
///
/// Err(StatusCode::UNAUTHORIZED)
/// # Ok(Self)
/// // ...
/// }
/// }
///
/// async fn handler() {
/// // If we get here the request has been authorized
/// }
/// async fn handler() {}
///
/// async fn other_handler() {
/// // If we get here the request has been authorized
/// }
/// async fn other_handler() {}
///
/// let app = Router::new()
/// .route("/", get(handler))
/// .route("/foo", post(other_handler))
/// // The extractor will run before all routes
/// .layer(extractor_middleware::<RequireAuth>());
/// .layer(extractor_middleware::<MyExtractor>());
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };

View file

@ -1,159 +1,4 @@
//! Types and traits for extracting data from requests.
//!
//! A handler function is an async function that takes any number of
//! "extractors" as arguments. An extractor is a type that implements
//! [`FromRequest`](crate::extract::FromRequest).
//!
//! 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<CreateUser>) {
//! // ...
//! }
//!
//! let app = Router::new().route("/users", post(create_user));
//! # async {
//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
//! # };
//! ```
//!
//! # Defining custom extractors
//!
//! You can also define your own extractors by implementing [`FromRequest`]:
//!
//! ```rust,no_run
//! use axum::{
//! async_trait,
//! extract::{FromRequest, RequestParts},
//! routing::get,
//! Router,
//! };
//! use http::{StatusCode, header::{HeaderValue, USER_AGENT}};
//!
//! struct ExtractUserAgent(HeaderValue);
//!
//! #[async_trait]
//! impl<B> FromRequest<B> for ExtractUserAgent
//! where
//! B: Send,
//! {
//! type Rejection = (StatusCode, &'static str);
//!
//! async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
//! let user_agent = req.headers().and_then(|headers| headers.get(USER_AGENT));
//!
//! if let Some(user_agent) = 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));
//! # async {
//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
//! # };
//! ```
//!
//! Note that only one extractor can consume the request body. If multiple body extractors are
//! applied a `500 Internal Server Error` response will be returned.
//!
//! # Request body extractors
//!
//! Most of the time your request body type will be [`body::Body`] (a re-export
//! of [`hyper::Body`]), which is directly supported by all extractors.
//!
//! However if you're applying a tower middleware that changes the response you
//! might have to apply a different body type to some extractors:
//!
//! ```rust
//! use std::{
//! task::{Context, Poll},
//! pin::Pin,
//! };
//! use tower_http::map_request_body::MapRequestBodyLayer;
//! use axum::{
//! extract::{self, BodyStream},
//! body::Body,
//! routing::get,
//! http::{header::HeaderMap, Request},
//! Router,
//! };
//!
//! struct MyBody<B>(B);
//!
//! impl<B> http_body::Body for MyBody<B>
//! where
//! B: http_body::Body + Unpin,
//! {
//! type Data = B::Data;
//! type Error = B::Error;
//!
//! fn poll_data(
//! mut self: Pin<&mut Self>,
//! cx: &mut Context<'_>,
//! ) -> Poll<Option<Result<Self::Data, Self::Error>>> {
//! Pin::new(&mut self.0).poll_data(cx)
//! }
//!
//! fn poll_trailers(
//! mut self: Pin<&mut Self>,
//! cx: &mut Context<'_>,
//! ) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
//! Pin::new(&mut self.0).poll_trailers(cx)
//! }
//! }
//!
//! let app = Router::new()
//! .route(
//! "/string",
//! // `String` works directly with any body type
//! get(|_: String| async {})
//! )
//! .route(
//! "/body",
//! // `extract::Body` defaults to `axum::body::Body`
//! // but can be customized
//! get(|_: extract::RawBody<MyBody<Body>>| async {})
//! )
//! .route(
//! "/body-stream",
//! // same for `extract::BodyStream`
//! get(|_: extract::BodyStream| async {}),
//! )
//! .route(
//! // and `Request<_>`
//! "/request",
//! get(|_: Request<MyBody<Body>>| async {})
//! )
//! // middleware that changes the request body type
//! .layer(MapRequestBodyLayer::new(MyBody));
//! # async {
//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
//! # };
//! ```
//!
//! [`body::Body`]: crate::body::Body
#![doc = include_str!("../docs/extract.md")]
use crate::{response::IntoResponse, Error};
use async_trait::async_trait;

View file

@ -1,10 +1,77 @@
//! Async functions that can be used to handle requests.
//!
#![doc = include_str!("../docs/handlers_intro.md")]
//!
//! 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.
//! //
//! // This works because `Bytes` implements `FromRequest`
//! // and therefore can be used as an extractor.
//! //
//! // `String` and `StatusCode` both implement `IntoResponse` and
//! // therefore `Result<String, StatusCode>` also implements `IntoResponse`
//! async fn echo(body: Bytes) -> Result<String, StatusCode> {
//! 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<B, T>,
//! | ------------- 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.
use crate::{
body::{box_body, BoxBody},
extract::{FromRequest, RequestParts},
response::IntoResponse,
routing::{MethodNotAllowed, MethodRouter},
BoxError,
};
use async_trait::async_trait;
@ -55,6 +122,10 @@ pub trait Handler<B, T>: Clone + Send + Sized + 'static {
/// Note this differs from [`routing::Router::layer`](crate::routing::Router::layer)
/// which adds a middleware to a group of routes.
///
/// If you're applying middleware that produces errors you have to handle the errors
/// so they're converted into responses. You can learn more about doing that
/// [here](crate::error_handling).
///
/// # Example
///
/// Adding the [`tower::limit::ConcurrencyLimit`] middleware to a handler
@ -78,9 +149,9 @@ pub trait Handler<B, T>: Clone + Send + Sized + 'static {
/// ```
fn layer<L>(self, layer: L) -> Layered<L::Service, T>
where
L: Layer<MethodRouter<Self, B, T, MethodNotAllowed>>,
L: Layer<IntoService<Self, B, T>>,
{
Layered::new(layer.layer(crate::routing::any(self)))
Layered::new(layer.layer(self.into_service()))
}
/// Convert the handler into a [`Service`].
@ -239,6 +310,7 @@ impl<S, T> Layered<S, T> {
#[test]
fn traits() {
use crate::routing::MethodRouter;
use crate::tests::*;
assert_send::<MethodRouter<(), NotSendSync, NotSendSync, ()>>();
assert_sync::<MethodRouter<(), NotSendSync, NotSendSync, ()>>();

View file

@ -1,20 +1,245 @@
//! axum is a web application framework that focuses on ergonomics and modularity.
//!
#![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")]
//! # Table of contents
//!
//! - [High level features](#high-level-features)
//! - [Compatibility](#compatibility)
//! - [Example](#example)
//! - [Routing](#routing)
//! - [Handlers](#handlers)
//! - [Extractors](#extractors)
//! - [Responses](#responses)
//! - [Error handling](#error-handling)
//! - [Middleware](#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();
//! }
//! ```
//!
//! # Routing
//!
//! [`Router`] is used to setup which paths goes to which services:
//!
//! ```rust
//! use axum::{Router, routing::get};
//!
//! // our router
//! let app = Router::new()
//! .route("/", get(root))
//! .route("/foo", get(foo))
//! .route("/foo/bar", get(foo_bar));
//!
//! // which calls one of these handlers
//! async fn root() {}
//! async fn foo() {}
//! async fn foo_bar() {}
//! # async {
//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
//! # };
//! ```
//!
//! See [`Router`] for more details on routing.
//!
//! # Handlers
//!
#![doc = include_str!("docs/handlers_intro.md")]
//!
//! See [`handler`](crate::handler) for more details on handlers.
//!
//! # 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.
//!
//! ```rust
//! use axum::extract::{Path, Query, Json};
//! use std::collections::HashMap;
//!
//! // `Path` gives you the path parameters and deserializes them.
//! async fn path(Path(user_id): Path<u32>) {}
//!
//! // `Query` gives you the query parameters and deserializes them.
//! async fn query(Query(params): Query<HashMap<String, String>>) {}
//!
//! // Buffer the request body and deserialize it as JSON into a
//! // `serde_json::Value`. `Json` supports any type that implements
//! // `serde::Deserialize`.
//! async fn json(Json(payload): Json<serde_json::Value>) {}
//! ```
//!
//! See [`extract`](crate::extract) for more details on extractors.
//!
//! # Responses
//!
//! Anything that implements [`IntoResponse`] can be returned from handlers.
//!
//! ```rust,no_run
//! use axum::{
//! body::Body,
//! routing::get,
//! response::Json,
//! Router,
//! };
//! use serde_json::{Value, json};
//!
//! // `&'static str` becomes a `200 OK` with `content-type: text/plain`
//! async fn plain_text() -> &'static str {
//! "foo"
//! }
//!
//! // `Json` gives a content-type of `application/json` and works with any type
//! // that implements `serde::Serialize`
//! async fn json() -> Json<Value> {
//! Json(json!({ "data": 42 }))
//! }
//!
//! let app = Router::new()
//! .route("/plain_text", get(plain_text))
//! .route("/json", get(json));
//! # async {
//! # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
//! # };
//! ```
//!
//! See [`response`](crate::response) for more details on building responses.
//!
//! # Error handling
//!
//! axum aims to have a simple and predictable error handling model. That means
//! it is simple to convert errors into responses and you are guaranteed that
//! all errors are handled.
//!
//! See [`error_handling`](crate::error_handling) for more details on axum's
//! error handling model and how to handle errors gracefully.
//!
//! # Middleware
//!
#![doc = include_str!("docs/middleware.md")]
//!
//! # 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 [`Extension`](crate::extract::Extension) extractor:
//!
//! ```rust,no_run
//! use axum::{
//! AddExtensionLayer,
//! extract::Extension,
//! 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(
//! Extension(state): Extension<Arc<State>>,
//! ) {
//! // ...
//! }
//! # 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 = "<latest-version>"
//! hyper = { version = "<latest-version>", features = ["full"] }
//! tokio = { version = "<latest-version>", features = ["full"] }
//! tower = "<latest-version>"
//! ```
//!
//! 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:
//!
//! Name | Description | Default?
//! ---|---|---
//! `headers` | Enables extracting typed headers via [`TypedHeader`] | No
//! `http1` | Enables hyper's `http1` feature | Yes
//! `http2` | Enables hyper's `http2` feature | No
//! `json` | Enables the [`Json`] type and some similar convenience functionality | Yes
//! `multipart` | Enables parsing `multipart/form-data` requests with [`Multipart`] | No
//! `tower-log` | Enables `tower`'s `log` feature | Yes
//! `ws` | Enables WebSockets support via [`extract::ws`] | No
//!
//! [`TypedHeader`]: crate::extract::TypedHeader
//! [`Multipart`]: crate::extract::Multipart
//! [`tower`]: https://crates.io/crates/tower
//! [`tower-http`]: https://crates.io/crates/tower-http
//! [`tokio`]: http://crates.io/crates/tokio

View file

@ -1,4 +1,4 @@
//! Types and traits for generating responses.
#![doc = include_str!("../docs/response.md")]
use crate::{
body::{box_body, BoxBody},

View file

@ -7,7 +7,7 @@ use std::{convert::Infallible, future::ready};
use tower::util::Oneshot;
pub use super::{
into_make_service::IntoMakeService, method_not_allowed::MethodNotAllowedFuture,
into_make_service::IntoMakeServiceFuture, method_not_allowed::MethodNotAllowedFuture,
route::RouteFuture,
};

View file

@ -1,5 +1,7 @@
use super::Router;
use std::{
convert::Infallible,
fmt,
future::ready,
task::{Context, Poll},
};
@ -8,24 +10,36 @@ use tower_service::Service;
/// A [`MakeService`] that produces axum router services.
///
/// [`MakeService`]: tower::make::MakeService
#[derive(Debug, Clone)]
pub struct IntoMakeService<S> {
service: S,
pub struct IntoMakeService<B> {
router: Router<B>,
}
impl<S> IntoMakeService<S> {
pub(super) fn new(service: S) -> Self {
Self { service }
impl<B> IntoMakeService<B> {
pub(super) fn new(router: Router<B>) -> Self {
Self { router }
}
}
impl<S, T> Service<T> for IntoMakeService<S>
where
S: Clone,
{
type Response = S;
impl<B> Clone for IntoMakeService<B> {
fn clone(&self) -> Self {
Self {
router: self.router.clone(),
}
}
}
impl<B> fmt::Debug for IntoMakeService<B> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("IntoMakeService")
.field("router", &self.router)
.finish()
}
}
impl<B, T> Service<T> for IntoMakeService<B> {
type Response = Router<B>;
type Error = Infallible;
type Future = MakeRouteServiceFuture<S>;
type Future = IntoMakeServiceFuture<B>;
#[inline]
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
@ -33,25 +47,25 @@ where
}
fn call(&mut self, _target: T) -> Self::Future {
MakeRouteServiceFuture::new(ready(Ok(self.service.clone())))
IntoMakeServiceFuture::new(ready(Ok(self.router.clone())))
}
}
opaque_future! {
/// Response future from [`MakeRouteService`] services.
pub type MakeRouteServiceFuture<S> =
std::future::Ready<Result<S, Infallible>>;
/// Response future for [`IntoMakeService`].
pub type IntoMakeServiceFuture<B> =
std::future::Ready<Result<Router<B>, Infallible>>;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::body::Body;
#[test]
fn traits() {
use crate::tests::*;
assert_send::<IntoMakeService<()>>();
assert_sync::<IntoMakeService<()>>();
assert_send::<IntoMakeService<Body>>();
}
}

View file

@ -27,8 +27,8 @@ use tower_layer::Layer;
use tower_service::Service;
pub mod future;
pub mod handler_method_router;
pub mod service_method_router;
pub mod handler_method_routing;
pub mod service_method_routing;
mod into_make_service;
mod method_filter;
@ -43,7 +43,7 @@ pub use self::{
};
#[doc(no_inline)]
pub use self::handler_method_router::{
pub use self::handler_method_routing::{
any, delete, get, head, on, options, patch, post, put, trace, MethodRouter,
};
@ -103,7 +103,7 @@ where
{
/// Create a new `Router`.
///
/// Unless you add additional routes this will respond to `404 Not Found` to
/// Unless you add additional routes this will respond with `404 Not Found` to
/// all requests.
pub fn new() -> Self {
Self {
@ -113,81 +113,8 @@ where
}
}
/// Add another route to the router.
///
/// `path` is a string of path segments separated by `/`. Each segment
/// can be either concrete, a capture, or a wildcard:
///
/// - `/foo/bar/baz` will only match requests where the path is `/foo/bar/bar`.
/// - `/:foo` will match any route with exactly one segment _and_ it will
/// capture the first segment and store it at the key `foo`.
/// - `/foo/bar/*rest` will match all requests that start with `/foo/bar`
/// and any number of segments after that. It will also create a capture
/// with the key `rest` that contains the matched segments.
///
/// `service` is the [`Service`] that should receive the request if the path
/// matches `path`.
///
/// # Example
///
/// ```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() { /* ... */ }
///
/// async fn do_users_action() { /* ... */ }
///
/// async fn serve_asset(Path(path): Path<String>) { /* ... */ }
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
///
/// # Panics
///
/// Panics if the route overlaps with another route:
///
/// ```should_panic
/// use axum::{routing::get, Router};
///
/// let app = Router::new()
/// .route("/", get(|| async {}))
/// .route("/", get(|| async {}));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
///
/// This also applies to `nest` which is similar to a wildcard route:
///
/// ```should_panic
/// use axum::{routing::get, Router};
///
/// let app = Router::new()
/// // this is similar to `/api/*`
/// .nest("/api", get(|| async {}))
/// // which overlaps with this route
/// .route("/api/users", get(|| async {}));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
///
/// Also panics if `path` is empty.
pub fn route<T>(mut self, path: &str, svc: T) -> Self
#[doc = include_str!("../docs/routing/route.md")]
pub fn route<T>(mut self, path: &str, service: T) -> Self
where
T: Service<Request<B>, Response = Response<BoxBody>, Error = Infallible>
+ Clone
@ -199,7 +126,7 @@ where
panic!("Invalid route: empty path");
}
let svc = match try_downcast::<Router<B>, _>(svc) {
let service = match try_downcast::<Router<B>, _>(service) {
Ok(_) => {
panic!("Invalid route: `Router::route` cannot be used with `Router`s. Use `Router::nest` instead")
}
@ -212,130 +139,12 @@ where
panic!("Invalid route: {}", err);
}
self.routes.insert(id, Route::new(svc));
self.routes.insert(id, Route::new(service));
self
}
/// Nest a group of routes (or a [`Service`]) at some path.
///
/// This allows you to break your application into smaller pieces and compose
/// them together.
///
/// ```
/// use axum::{
/// routing::get,
/// Router,
/// };
/// use http::Uri;
///
/// async fn users_get(uri: Uri) {
/// // `uri` will be `/users` since `nest` strips the matching prefix.
/// // use `OriginalUri` to always get the full URI.
/// }
///
/// async fn users_post() {}
///
/// async fn careers() {}
///
/// let users_api = Router::new().route("/users", get(users_get).post(users_post));
///
/// let app = Router::new()
/// .nest("/api", users_api)
/// .route("/careers", get(careers));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
///
/// 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.
///
/// Take care when using `nest` together with dynamic routes as nesting also
/// captures from the outer routes:
///
/// ```
/// use axum::{
/// extract::Path,
/// routing::get,
/// Router,
/// };
/// use std::collections::HashMap;
///
/// async fn users_get(Path(params): Path<HashMap<String, String>>) {
/// // Both `version` and `id` were captured even though `users_api` only
/// // explicitly captures `id`.
/// let version = params.get("version");
/// let id = params.get("id");
/// }
///
/// let users_api = Router::new().route("/users/:id", get(users_get));
///
/// let app = Router::new().nest("/:version/api", users_api);
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
///
/// `nest` also accepts any [`Service`]. This can for example be used with
/// [`tower_http::services::ServeDir`] to serve static files from a directory:
///
/// ```
/// use axum::{
/// Router,
/// routing::service_method_router::get,
/// error_handling::HandleErrorExt,
/// http::StatusCode,
/// };
/// use std::{io, convert::Infallible};
/// use tower_http::services::ServeDir;
///
/// // Serves files inside the `public` directory at `GET /public/*`
/// let serve_dir_service = ServeDir::new("public")
/// .handle_error(|error: io::Error| {
/// (
/// StatusCode::INTERNAL_SERVER_ERROR,
/// format!("Unhandled internal error: {}", error),
/// )
/// });
///
/// let app = Router::new().nest("/public", get(serve_dir_service));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
///
/// # Differences to 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 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();
/// # };
/// ```
///
/// # 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
#[doc = include_str!("../docs/routing/nest.md")]
pub fn nest<T>(mut self, path: &str, svc: T) -> Self
where
T: Service<Request<B>, Response = Response<BoxBody>, Error = Infallible>
@ -362,6 +171,7 @@ where
let Router {
mut routes,
node,
// discard the fallback of the nested router
fallback: _,
} = router;
@ -392,73 +202,33 @@ where
self
}
/// Apply a [`tower::Layer`] to the router.
///
/// All requests to the router will be processed by the layer's
/// corresponding middleware.
///
/// This can be used to add additional processing to a request for a group
/// of routes.
///
/// Note this differs from [`handler::Layered`](crate::handler::Layered)
/// which adds a middleware to a single handler.
///
/// # Example
///
/// Adding the [`tower::limit::ConcurrencyLimit`] middleware to a group of
/// routes can be done like so:
///
/// ```rust
/// use axum::{
/// routing::get,
/// Router,
/// };
/// use tower::limit::{ConcurrencyLimitLayer, ConcurrencyLimit};
///
/// async fn first_handler() { /* ... */ }
///
/// async fn second_handler() { /* ... */ }
///
/// async fn third_handler() { /* ... */ }
///
/// // All requests to `handler` and `other_handler` will be sent through
/// // `ConcurrencyLimit`
/// let app = Router::new().route("/", get(first_handler))
/// .route("/foo", get(second_handler))
/// .layer(ConcurrencyLimitLayer::new(64))
/// // Request to `GET /bar` will go directly to `third_handler` and
/// // wont be sent through `ConcurrencyLimit`
/// .route("/bar", get(third_handler));
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
///
/// This is commonly used to add middleware such as tracing/logging to your
/// entire app:
///
/// ```rust
/// use axum::{
/// routing::get,
/// Router,
/// };
/// use tower_http::trace::TraceLayer;
///
/// async fn first_handler() { /* ... */ }
///
/// async fn second_handler() { /* ... */ }
///
/// async fn third_handler() { /* ... */ }
///
/// let app = Router::new()
/// .route("/", get(first_handler))
/// .route("/foo", get(second_handler))
/// .route("/bar", get(third_handler))
/// .layer(TraceLayer::new_for_http());
/// # async {
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
#[doc = include_str!("../docs/routing/merge.md")]
pub fn merge(mut self, other: Router<B>) -> Self {
let Router {
routes,
node,
fallback,
} = other;
if let Err(err) = self.node.merge(node) {
panic!("Invalid route: {}", err);
}
for (id, route) in routes {
assert!(self.routes.insert(id, route).is_none());
}
self.fallback = match (self.fallback, fallback) {
(Fallback::Default(_), pick @ Fallback::Default(_)) => pick,
(Fallback::Default(_), pick @ Fallback::Custom(_)) => pick,
(pick @ Fallback::Custom(_), Fallback::Default(_)) => pick,
(Fallback::Custom(_), pick @ Fallback::Custom(_)) => pick,
};
self
}
#[doc = include_str!("../docs/routing/layer.md")]
pub fn layer<L, LayeredReqBody, LayeredResBody>(self, layer: L) -> Router<LayeredReqBody>
where
L: Layer<Route<B>>,
@ -496,6 +266,19 @@ where
}
}
#[doc = include_str!("../docs/routing/fallback.md")]
pub fn fallback<T>(mut self, svc: T) -> Self
where
T: Service<Request<B>, Response = Response<BoxBody>, Error = Infallible>
+ Clone
+ Send
+ 'static,
T::Future: Send + 'static,
{
self.fallback = Fallback::Custom(Route::new(svc));
self
}
/// Convert this router into a [`MakeService`], that is a [`Service`] who's
/// response is another service.
///
@ -519,234 +302,20 @@ where
/// ```
///
/// [`MakeService`]: tower::make::MakeService
pub fn into_make_service(self) -> IntoMakeService<Self> {
pub fn into_make_service(self) -> IntoMakeService<B> {
IntoMakeService::new(self)
}
/// 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:
///
/// ```
/// use axum::{
/// extract::ConnectInfo,
/// routing::get,
/// Router,
/// };
/// use std::net::SocketAddr;
///
/// let app = Router::new().route("/", get(handler));
///
/// async fn handler(ConnectInfo(addr): ConnectInfo<SocketAddr>) -> String {
/// format!("Hello {}", addr)
/// }
///
/// # async {
/// axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
/// .serve(
/// app.into_make_service_with_connect_info::<SocketAddr, _>()
/// )
/// .await
/// .expect("server failed");
/// # };
/// ```
///
/// You can implement custom a [`Connected`] like so:
///
/// ```
/// use axum::{
/// extract::connect_info::{ConnectInfo, Connected},
/// routing::get,
/// Router,
/// };
/// use hyper::server::conn::AddrStream;
///
/// let app = Router::new().route("/", get(handler));
///
/// async fn handler(
/// ConnectInfo(my_connect_info): ConnectInfo<MyConnectInfo>,
/// ) -> String {
/// format!("Hello {:?}", my_connect_info)
/// }
///
/// #[derive(Clone, Debug)]
/// struct MyConnectInfo {
/// // ...
/// }
///
/// impl Connected<&AddrStream> for MyConnectInfo {
/// fn connect_info(target: &AddrStream) -> Self {
/// MyConnectInfo {
/// // ...
/// }
/// }
/// }
///
/// # async {
/// axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
/// .serve(
/// app.into_make_service_with_connect_info::<MyConnectInfo, _>()
/// )
/// .await
/// .expect("server failed");
/// # };
/// ```
///
/// 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.rs
#[doc = include_str!("../docs/routing/into_make_service_with_connect_info.md")]
pub fn into_make_service_with_connect_info<C, Target>(
self,
) -> IntoMakeServiceWithConnectInfo<Self, C>
) -> IntoMakeServiceWithConnectInfo<B, C>
where
C: Connected<Target>,
{
IntoMakeServiceWithConnectInfo::new(self)
}
/// Merge two routers into one.
///
/// This is useful for breaking apps into smaller pieces and combining them
/// into one.
///
/// ```
/// 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 = user_routes.merge(team_routes);
/// # async {
/// # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
pub fn merge(mut self, other: Router<B>) -> Self {
let Router {
routes,
node,
fallback,
} = other;
if let Err(err) = self.node.merge(node) {
panic!("Invalid route: {}", err);
}
for (id, route) in routes {
assert!(self.routes.insert(id, route).is_none());
}
self.fallback = match (self.fallback, fallback) {
(Fallback::Default(_), pick @ Fallback::Default(_)) => pick,
(Fallback::Default(_), pick @ Fallback::Custom(_)) => pick,
(pick @ Fallback::Custom(_), Fallback::Default(_)) => pick,
(Fallback::Custom(_), pick @ Fallback::Custom(_)) => pick,
};
self
}
/// 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, Uri},
/// };
///
/// let app = Router::new()
/// .route("/foo", get(|| async { /* ... */ }))
/// .fallback(fallback.into_service());
///
/// async fn fallback(uri: Uri) -> impl IntoResponse {
/// (StatusCode::NOT_FOUND, format!("No route for {}", uri))
/// }
/// # async {
/// # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
///
/// 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.
///
/// ## When used with `Router::merge`
///
/// If a router with a fallback is merged with another router that also has
/// a fallback the fallback of the second router will be used:
///
/// ```rust
/// use axum::{
/// Router,
/// routing::get,
/// handler::Handler,
/// response::IntoResponse,
/// http::{StatusCode, Uri},
/// };
///
/// let one = Router::new()
/// .route("/one", get(|| async { /* ... */ }))
/// .fallback(fallback_one.into_service());
///
/// let two = Router::new()
/// .route("/two", get(|| async { /* ... */ }))
/// .fallback(fallback_two.into_service());
///
/// let app = one.merge(two);
///
/// async fn fallback_one() -> impl IntoResponse { /* ... */ }
/// async fn fallback_two() -> impl IntoResponse { /* ... */ }
///
/// // the fallback for `app` is `fallback_two`
/// # async {
/// # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
///
/// If only one of the routers have a fallback that will be used in the
/// merged router.
///
/// ## When used with `Router::nest`
///
/// If a router with a fallback is nested inside another router the fallback
/// of the nested router will be discarded and not used. This is such that
/// the outer router's fallback takes precedence.
pub fn fallback<T>(mut self, svc: T) -> Self
where
T: Service<Request<B>, Response = Response<BoxBody>, Error = Infallible>
+ Clone
+ Send
+ 'static,
T::Future: Send + 'static,
{
self.fallback = Fallback::Custom(Route::new(svc));
self
}
#[inline]
fn call_route(&self, match_: matchit::Match<&RouteId>, mut req: Request<B>) -> RouterFuture<B> {
let id = *match_.value;

View file

@ -16,7 +16,8 @@ use tower_service::Service;
/// How routes are stored inside a [`Router`](super::Router).
///
/// You normally shouldn't need to care about this type.
/// You normally shouldn't need to care about this type. Its used in
/// [`Router::layer`](super::Router::layer).
pub struct Route<B = Body>(CloneBoxService<Request<B>, Response<BoxBody>, Infallible>);
impl<B> Route<B> {

View file

@ -13,7 +13,7 @@
//! use tower_http::services::Redirect;
//! use axum::{
//! body::Body,
//! routing::{get, service_method_router as service},
//! routing::{get, service_method_routing as service},
//! http::Request,
//! Router,
//! };
@ -148,7 +148,7 @@ where
/// use axum::{
/// http::Request,
/// Router,
/// routing::service_method_router as service,
/// routing::service_method_routing as service,
/// };
/// use http::Response;
/// use std::convert::Infallible;
@ -244,7 +244,7 @@ where
/// http::Request,
/// routing::on,
/// Router,
/// routing::{MethodFilter, service_method_router as service},
/// routing::{MethodFilter, service_method_routing as service},
/// };
/// use http::Response;
/// use std::convert::Infallible;
@ -327,7 +327,7 @@ impl<S, F, B> MethodRouter<S, F, B> {
/// use axum::{
/// http::Request,
/// Router,
/// routing::{MethodFilter, on, service_method_router as service},
/// routing::{MethodFilter, on, service_method_routing as service},
/// };
/// use http::Response;
/// use std::convert::Infallible;
@ -428,7 +428,7 @@ impl<S, F, B> MethodRouter<S, F, B> {
/// use axum::{
/// http::Request,
/// Router,
/// routing::{MethodFilter, on, service_method_router as service},
/// routing::{MethodFilter, on, service_method_routing as service},
/// };
/// use http::Response;
/// use std::convert::Infallible;

View file

@ -38,7 +38,7 @@ mod for_handlers {
mod for_services {
use super::*;
use crate::routing::service_method_router::get;
use crate::routing::service_method_routing::get;
use http::header::HeaderValue;
#[tokio::test]

View file

@ -196,7 +196,7 @@ async fn many_ors() {
#[tokio::test]
async fn services() {
use crate::routing::service_method_router::get;
use crate::routing::service_method_routing::get;
let app = Router::new()
.route(

View file

@ -7,7 +7,7 @@ use crate::{
extract::{self, Path},
handler::Handler,
response::IntoResponse,
routing::{any, delete, get, on, patch, post, service_method_router as service, MethodFilter},
routing::{any, delete, get, on, patch, post, service_method_routing as service, MethodFilter},
Json, Router,
};
use bytes::Bytes;