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