mirror of
https://github.com/tokio-rs/axum.git
synced 2026-09-07 00:00:12 +02:00
Docs improvements (#37)
This commit is contained in:
@@ -19,6 +19,7 @@ use http::StatusCode;
|
|||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
|
convert::Infallible,
|
||||||
net::SocketAddr,
|
net::SocketAddr,
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
@@ -152,20 +153,20 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_error(error: BoxError) -> impl IntoResponse {
|
fn handle_error(error: BoxError) -> Result<impl IntoResponse, Infallible> {
|
||||||
if error.is::<tower::timeout::error::Elapsed>() {
|
if error.is::<tower::timeout::error::Elapsed>() {
|
||||||
return (StatusCode::REQUEST_TIMEOUT, Cow::from("request timed out"));
|
return Ok((StatusCode::REQUEST_TIMEOUT, Cow::from("request timed out")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if error.is::<tower::load_shed::error::Overloaded>() {
|
if error.is::<tower::load_shed::error::Overloaded>() {
|
||||||
return (
|
return Ok((
|
||||||
StatusCode::SERVICE_UNAVAILABLE,
|
StatusCode::SERVICE_UNAVAILABLE,
|
||||||
Cow::from("service is overloaded, try again later"),
|
Cow::from("service is overloaded, try again later"),
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
(
|
Ok((
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
Cow::from(format!("Unhandled internal error: {}", error)),
|
Cow::from(format!("Unhandled internal error: {}", error)),
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ async fn main() {
|
|||||||
let app = axum::routing::nest(
|
let app = axum::routing::nest(
|
||||||
"/static",
|
"/static",
|
||||||
axum::service::get(ServeDir::new(".").handle_error(|error: std::io::Error| {
|
axum::service::get(ServeDir::new(".").handle_error(|error: std::io::Error| {
|
||||||
(
|
Ok::<_, std::convert::Infallible>((
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
format!("Unhandled interal error: {}", error),
|
format!("Unhandled interal error: {}", error),
|
||||||
)
|
))
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
.layer(TraceLayer::new_for_http());
|
.layer(TraceLayer::new_for_http());
|
||||||
|
|||||||
@@ -33,10 +33,10 @@ async fn main() {
|
|||||||
ServeDir::new("examples/websocket")
|
ServeDir::new("examples/websocket")
|
||||||
.append_index_html_on_directories(true)
|
.append_index_html_on_directories(true)
|
||||||
.handle_error(|error: std::io::Error| {
|
.handle_error(|error: std::io::Error| {
|
||||||
(
|
Ok::<_, std::convert::Infallible>((
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
format!("Unhandled interal error: {}", error),
|
format!("Unhandled interal error: {}", error),
|
||||||
)
|
))
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
+16
-6
@@ -796,12 +796,22 @@ where
|
|||||||
type Rejection = RequestAlreadyExtracted;
|
type Rejection = RequestAlreadyExtracted;
|
||||||
|
|
||||||
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||||
let all_parts = req
|
let RequestParts {
|
||||||
.method()
|
method,
|
||||||
.zip(req.uri())
|
uri,
|
||||||
.zip(req.headers())
|
version,
|
||||||
.zip(req.extensions())
|
headers,
|
||||||
.zip(req.body());
|
extensions,
|
||||||
|
body,
|
||||||
|
} = req;
|
||||||
|
|
||||||
|
let all_parts = method
|
||||||
|
.as_ref()
|
||||||
|
.zip(version.as_ref())
|
||||||
|
.zip(uri.as_ref())
|
||||||
|
.zip(extensions.as_ref())
|
||||||
|
.zip(body.as_ref())
|
||||||
|
.zip(headers.as_ref());
|
||||||
|
|
||||||
if all_parts.is_some() {
|
if all_parts.is_some() {
|
||||||
Ok(req.into_request())
|
Ok(req.into_request())
|
||||||
|
|||||||
+4
-75
@@ -1,42 +1,4 @@
|
|||||||
//! Async functions that can be used to handle requests.
|
//! Async functions that can be used to handle requests.
|
||||||
//!
|
|
||||||
//! # What is a handler?
|
|
||||||
//!
|
|
||||||
//! In axum a "handler" is an async function that accepts zero or more
|
|
||||||
//! ["extractors"](crate::extract) as arguments and returns something that
|
|
||||||
//! implements [`IntoResponse`].
|
|
||||||
//!
|
|
||||||
//! # Example
|
|
||||||
//!
|
|
||||||
//! Some examples of handlers:
|
|
||||||
//!
|
|
||||||
//! ```rust
|
|
||||||
//! use axum::prelude::*;
|
|
||||||
//! use bytes::Bytes;
|
|
||||||
//! use http::StatusCode;
|
|
||||||
//!
|
|
||||||
//! // Handler that immediately returns an empty `200 OK` response.
|
|
||||||
//! async fn unit_handler() {}
|
|
||||||
//!
|
|
||||||
//! // Handler that immediately returns an empty `200 Ok` response with a plain
|
|
||||||
//! /// text body.
|
|
||||||
//! async fn string_handler() -> String {
|
|
||||||
//! "Hello, World!".to_string()
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! // Handler that buffers the request body and returns it if it is valid UTF-8
|
|
||||||
//! async fn buffer_body(body: Bytes) -> Result<String, StatusCode> {
|
|
||||||
//! if let Ok(string) = String::from_utf8(body.to_vec()) {
|
|
||||||
//! Ok(string)
|
|
||||||
//! } else {
|
|
||||||
//! Err(StatusCode::BAD_REQUEST)
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! For more details on generating responses see the
|
|
||||||
//! [`response`](crate::response) module and for more details on extractors see
|
|
||||||
//! the [`extract`](crate::extract) module.
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
body::{box_body, BoxBody},
|
body::{box_body, BoxBody},
|
||||||
@@ -402,49 +364,16 @@ impl<S, T> Layered<S, T> {
|
|||||||
/// This is used to convert errors to responses rather than simply
|
/// This is used to convert errors to responses rather than simply
|
||||||
/// terminating the connection.
|
/// terminating the connection.
|
||||||
///
|
///
|
||||||
/// `handle_error` can be used like so:
|
/// It works similarly to [`routing::Layered::handle_error`]. See that for more details.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// [`routing::Layered::handle_error`]: crate::routing::Layered::handle_error
|
||||||
/// use axum::prelude::*;
|
pub fn handle_error<F, ReqBody, ResBody, Res, E>(
|
||||||
/// use http::StatusCode;
|
|
||||||
/// use tower::{BoxError, timeout::TimeoutLayer};
|
|
||||||
/// use std::time::Duration;
|
|
||||||
///
|
|
||||||
/// async fn handler() { /* ... */ }
|
|
||||||
///
|
|
||||||
/// // `Timeout` will fail with `BoxError` if the timeout elapses...
|
|
||||||
/// let layered_handler = handler
|
|
||||||
/// .layer(TimeoutLayer::new(Duration::from_secs(30)));
|
|
||||||
///
|
|
||||||
/// // ...so we should handle that error
|
|
||||||
/// let layered_handler = layered_handler.handle_error(|error: BoxError| {
|
|
||||||
/// if error.is::<tower::timeout::error::Elapsed>() {
|
|
||||||
/// (
|
|
||||||
/// StatusCode::REQUEST_TIMEOUT,
|
|
||||||
/// "request took too long".to_string(),
|
|
||||||
/// )
|
|
||||||
/// } else {
|
|
||||||
/// (
|
|
||||||
/// StatusCode::INTERNAL_SERVER_ERROR,
|
|
||||||
/// format!("Unhandled internal error: {}", error),
|
|
||||||
/// )
|
|
||||||
/// }
|
|
||||||
/// });
|
|
||||||
///
|
|
||||||
/// let app = route("/", get(layered_handler));
|
|
||||||
/// # async {
|
|
||||||
/// # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
|
||||||
/// # };
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// The closure can return any type that implements [`IntoResponse`].
|
|
||||||
pub fn handle_error<F, ReqBody, ResBody, Res>(
|
|
||||||
self,
|
self,
|
||||||
f: F,
|
f: F,
|
||||||
) -> Layered<HandleError<S, F, ReqBody>, T>
|
) -> Layered<HandleError<S, F, ReqBody>, T>
|
||||||
where
|
where
|
||||||
S: Service<Request<ReqBody>, Response = Response<ResBody>>,
|
S: Service<Request<ReqBody>, Response = Response<ResBody>>,
|
||||||
F: FnOnce(S::Error) -> Res,
|
F: FnOnce(S::Error) -> Result<Res, E>,
|
||||||
Res: IntoResponse,
|
Res: IntoResponse,
|
||||||
{
|
{
|
||||||
let svc = HandleError::new(self.svc, f);
|
let svc = HandleError::new(self.svc, f);
|
||||||
|
|||||||
+158
-168
@@ -1,12 +1,31 @@
|
|||||||
//! axum is a web application framework that focuses on ergonomics and modularity.
|
//! axum is a web application framework that focuses on ergonomics and modularity.
|
||||||
//!
|
//!
|
||||||
//! ## Goals
|
//! # Table of contents
|
||||||
|
//!
|
||||||
|
//! - [Goals](#goals)
|
||||||
|
//! - [Compatibility](#compatibility)
|
||||||
|
//! - [Handlers](#handlers)
|
||||||
|
//! - [Routing](#routing)
|
||||||
|
//! - [Extractors](#extractors)
|
||||||
|
//! - [Building responses](#building-responses)
|
||||||
|
//! - [Applying middleware](#applying-middleware)
|
||||||
|
//! - [To individual handlers](#to-individual-handlers)
|
||||||
|
//! - [To groups of routes](#to-groups-of-routes)
|
||||||
|
//! - [Error handling](#error-handling)
|
||||||
|
//! - [Sharing state with handlers](#sharing-state-with-handlers)
|
||||||
|
//! - [Routing to any `Service`](#routing-to-any-service)
|
||||||
|
//! - [Nesting applications](#nesting-applications)
|
||||||
|
//! - [Feature flags](#feature-flags)
|
||||||
|
//!
|
||||||
|
//! # Goals
|
||||||
//!
|
//!
|
||||||
//! - Ease of use. Building web apps in Rust should be as easy as `async fn
|
//! - Ease of use. Building web apps in Rust should be as easy as `async fn
|
||||||
//! handle(Request) -> Response`.
|
//! handle(Request) -> Response`.
|
||||||
//! - Solid foundation. axum is built on top of [tower] and [hyper] and makes it
|
//! - Solid foundation. axum is built on top of [tower] and [hyper] and makes it
|
||||||
//! easy to plug in any middleware from the [tower] and [tower-http] ecosystem.
|
//! easy to plug in any middleware from the [tower] and [tower-http] ecosystem.
|
||||||
//! - Focus on routing, extracting data from requests, and generating responses.
|
//! This improves compatibility since axum doesn't have its own custom
|
||||||
|
//! middleware system.
|
||||||
|
//! - Focus on routing, extracting data from requests, and building responses.
|
||||||
//! tower middleware can handle the rest.
|
//! tower middleware can handle the rest.
|
||||||
//! - Macro free core. Macro frameworks have their place but axum focuses
|
//! - Macro free core. Macro frameworks have their place but axum focuses
|
||||||
//! on providing a core that is macro free.
|
//! on providing a core that is macro free.
|
||||||
@@ -39,6 +58,41 @@
|
|||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
//! # Handlers
|
||||||
|
//!
|
||||||
|
//! In axum a "handler" is an async function that accepts zero or more
|
||||||
|
//! ["extractors"](#extractors) as arguments and returns something that
|
||||||
|
//! can be converted [into a response](#building-responses).
|
||||||
|
//!
|
||||||
|
//! Handlers is where you custom domain logic lives and axum applications are
|
||||||
|
//! built by routing between handlers.
|
||||||
|
//!
|
||||||
|
//! Some examples of handlers:
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! use axum::prelude::*;
|
||||||
|
//! use bytes::Bytes;
|
||||||
|
//! use http::StatusCode;
|
||||||
|
//!
|
||||||
|
//! // Handler that immediately returns an empty `200 OK` response.
|
||||||
|
//! async fn unit_handler() {}
|
||||||
|
//!
|
||||||
|
//! // Handler that immediately returns an empty `200 Ok` response with a plain
|
||||||
|
//! // text body.
|
||||||
|
//! async fn string_handler() -> String {
|
||||||
|
//! "Hello, World!".to_string()
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! // Handler that buffers the request body and returns it if it is valid UTF-8
|
||||||
|
//! async fn buffer_body(body: Bytes) -> Result<String, StatusCode> {
|
||||||
|
//! if let Ok(string) = String::from_utf8(body.to_vec()) {
|
||||||
|
//! Ok(string)
|
||||||
|
//! } else {
|
||||||
|
//! Err(StatusCode::BAD_REQUEST)
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
//! # Routing
|
//! # Routing
|
||||||
//!
|
//!
|
||||||
//! Routing between handlers looks like this:
|
//! Routing between handlers looks like this:
|
||||||
@@ -65,92 +119,12 @@
|
|||||||
//! # };
|
//! # };
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Routes can also be dynamic like `/users/:id`. See ["Extracting data from
|
//! Routes can also be dynamic like `/users/:id`.
|
||||||
//! requests"](#extracting-data-from-requests) for more details on that.
|
|
||||||
//!
|
//!
|
||||||
//! # Responses
|
//! # Extractors
|
||||||
//!
|
//!
|
||||||
//! Anything that implements [`IntoResponse`](response::IntoResponse) can be
|
//! An extractor is a type that implements [`FromRequest`]. Extractors is how
|
||||||
//! returned from a handler:
|
//! you pick apart the incoming request to get the parts your handler needs.
|
||||||
//!
|
|
||||||
//! ```rust,no_run
|
|
||||||
//! use axum::{body::Body, response::{Html, Json}, prelude::*};
|
|
||||||
//! use http::{StatusCode, Response, Uri};
|
|
||||||
//! use serde_json::{Value, json};
|
|
||||||
//!
|
|
||||||
//! // We've already seen returning &'static str
|
|
||||||
//! async fn plain_text() -> &'static str {
|
|
||||||
//! "foo"
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! // String works too and will get a text/plain content-type
|
|
||||||
//! async fn plain_text_string(uri: Uri) -> String {
|
|
||||||
//! format!("Hi from {}", uri.path())
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! // Bytes will get a `application/octet-stream` content-type
|
|
||||||
//! async fn bytes() -> Vec<u8> {
|
|
||||||
//! vec![1, 2, 3, 4]
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! // `()` gives an empty response
|
|
||||||
//! async fn empty() {}
|
|
||||||
//!
|
|
||||||
//! // `StatusCode` gives an empty response with that status code
|
|
||||||
//! async fn empty_with_status() -> StatusCode {
|
|
||||||
//! StatusCode::NOT_FOUND
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! // A tuple of `StatusCode` and something that implements `IntoResponse` can
|
|
||||||
//! // be used to override the status code
|
|
||||||
//! async fn with_status() -> (StatusCode, &'static str) {
|
|
||||||
//! (StatusCode::INTERNAL_SERVER_ERROR, "Something went wrong")
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! // `Html` gives a content-type of `text/html`
|
|
||||||
//! async fn html() -> Html<&'static str> {
|
|
||||||
//! Html("<h1>Hello, World!</h1>")
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! // `Json` gives a content-type of `application/json` and works with any type
|
|
||||||
//! // that implements `serde::Serialize`
|
|
||||||
//! async fn json() -> Json<Value> {
|
|
||||||
//! Json(json!({ "data": 42 }))
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! // `Result<T, E>` where `T` and `E` implement `IntoResponse` is useful for
|
|
||||||
//! // returning errors
|
|
||||||
//! async fn result() -> Result<&'static str, StatusCode> {
|
|
||||||
//! Ok("all good")
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! // `Response` gives full control
|
|
||||||
//! async fn response() -> Response<Body> {
|
|
||||||
//! Response::builder().body(Body::empty()).unwrap()
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! let app = route("/plain_text", get(plain_text))
|
|
||||||
//! .route("/plain_text_string", get(plain_text_string))
|
|
||||||
//! .route("/bytes", get(bytes))
|
|
||||||
//! .route("/empty", get(empty))
|
|
||||||
//! .route("/empty_with_status", get(empty_with_status))
|
|
||||||
//! .route("/with_status", get(with_status))
|
|
||||||
//! .route("/html", get(html))
|
|
||||||
//! .route("/json", get(json))
|
|
||||||
//! .route("/result", get(result))
|
|
||||||
//! .route("/response", get(response));
|
|
||||||
//! # async {
|
|
||||||
//! # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
|
||||||
//! # };
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! See the [`response`] module for more details.
|
|
||||||
//!
|
|
||||||
//! # Extracting data from requests
|
|
||||||
//!
|
|
||||||
//! A handler function is an async function take takes any number of
|
|
||||||
//! "extractors" as arguments. An extractor is a type that implements
|
|
||||||
//! [`FromRequest`](crate::extract::FromRequest).
|
|
||||||
//!
|
//!
|
||||||
//! For example, [`extract::Json`] is an extractor that consumes the request
|
//! For example, [`extract::Json`] is an extractor that consumes the request
|
||||||
//! body and deserializes it as JSON into some target type:
|
//! body and deserializes it as JSON into some target type:
|
||||||
@@ -256,13 +230,90 @@
|
|||||||
//! See the [`extract`] module for more details.
|
//! See the [`extract`] module for more details.
|
||||||
//!
|
//!
|
||||||
//! [`Uuid`]: https://docs.rs/uuid/latest/uuid/
|
//! [`Uuid`]: https://docs.rs/uuid/latest/uuid/
|
||||||
|
//! [`FromRequest`]: crate::extract::FromRequest
|
||||||
|
//!
|
||||||
|
//! # Building responses
|
||||||
|
//!
|
||||||
|
//! Anything that implements [`IntoResponse`](response::IntoResponse) can be
|
||||||
|
//! returned from a handler:
|
||||||
|
//!
|
||||||
|
//! ```rust,no_run
|
||||||
|
//! use axum::{body::Body, response::{Html, Json}, prelude::*};
|
||||||
|
//! use http::{StatusCode, Response, Uri};
|
||||||
|
//! use serde_json::{Value, json};
|
||||||
|
//!
|
||||||
|
//! // We've already seen returning &'static str
|
||||||
|
//! async fn plain_text() -> &'static str {
|
||||||
|
//! "foo"
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! // String works too and will get a text/plain content-type
|
||||||
|
//! async fn plain_text_string(uri: Uri) -> String {
|
||||||
|
//! format!("Hi from {}", uri.path())
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! // Bytes will get a `application/octet-stream` content-type
|
||||||
|
//! async fn bytes() -> Vec<u8> {
|
||||||
|
//! vec![1, 2, 3, 4]
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! // `()` gives an empty response
|
||||||
|
//! async fn empty() {}
|
||||||
|
//!
|
||||||
|
//! // `StatusCode` gives an empty response with that status code
|
||||||
|
//! async fn empty_with_status() -> StatusCode {
|
||||||
|
//! StatusCode::NOT_FOUND
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! // A tuple of `StatusCode` and something that implements `IntoResponse` can
|
||||||
|
//! // be used to override the status code
|
||||||
|
//! async fn with_status() -> (StatusCode, &'static str) {
|
||||||
|
//! (StatusCode::INTERNAL_SERVER_ERROR, "Something went wrong")
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! // `Html` gives a content-type of `text/html`
|
||||||
|
//! async fn html() -> Html<&'static str> {
|
||||||
|
//! Html("<h1>Hello, World!</h1>")
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! // `Json` gives a content-type of `application/json` and works with any type
|
||||||
|
//! // that implements `serde::Serialize`
|
||||||
|
//! async fn json() -> Json<Value> {
|
||||||
|
//! Json(json!({ "data": 42 }))
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! // `Result<T, E>` where `T` and `E` implement `IntoResponse` is useful for
|
||||||
|
//! // returning errors
|
||||||
|
//! async fn result() -> Result<&'static str, StatusCode> {
|
||||||
|
//! Ok("all good")
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! // `Response` gives full control
|
||||||
|
//! async fn response() -> Response<Body> {
|
||||||
|
//! Response::builder().body(Body::empty()).unwrap()
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! let app = route("/plain_text", get(plain_text))
|
||||||
|
//! .route("/plain_text_string", get(plain_text_string))
|
||||||
|
//! .route("/bytes", get(bytes))
|
||||||
|
//! .route("/empty", get(empty))
|
||||||
|
//! .route("/empty_with_status", get(empty_with_status))
|
||||||
|
//! .route("/with_status", get(with_status))
|
||||||
|
//! .route("/html", get(html))
|
||||||
|
//! .route("/json", get(json))
|
||||||
|
//! .route("/result", get(result))
|
||||||
|
//! .route("/response", get(response));
|
||||||
|
//! # async {
|
||||||
|
//! # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||||
|
//! # };
|
||||||
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! # Applying middleware
|
//! # Applying middleware
|
||||||
//!
|
//!
|
||||||
//! axum is designed to take full advantage of the tower and tower-http
|
//! axum is designed to take full advantage of the tower and tower-http
|
||||||
//! ecosystem of middleware:
|
//! ecosystem of middleware:
|
||||||
//!
|
//!
|
||||||
//! ## Applying middleware to individual handlers
|
//! ## To individual handlers
|
||||||
//!
|
//!
|
||||||
//! A middleware can be applied to a single handler like so:
|
//! A middleware can be applied to a single handler like so:
|
||||||
//!
|
//!
|
||||||
@@ -281,7 +332,7 @@
|
|||||||
//! # };
|
//! # };
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! ## Applying middleware to groups of routes
|
//! ## To groups of routes
|
||||||
//!
|
//!
|
||||||
//! Middleware can also be applied to a group of routes like so:
|
//! Middleware can also be applied to a group of routes like so:
|
||||||
//!
|
//!
|
||||||
@@ -320,7 +371,7 @@
|
|||||||
//! use tower::{
|
//! use tower::{
|
||||||
//! BoxError, timeout::{TimeoutLayer, error::Elapsed},
|
//! BoxError, timeout::{TimeoutLayer, error::Elapsed},
|
||||||
//! };
|
//! };
|
||||||
//! use std::{borrow::Cow, time::Duration};
|
//! use std::{borrow::Cow, time::Duration, convert::Infallible};
|
||||||
//! use http::StatusCode;
|
//! use http::StatusCode;
|
||||||
//!
|
//!
|
||||||
//! let app = route(
|
//! let app = route(
|
||||||
@@ -332,16 +383,19 @@
|
|||||||
//! // Check if the actual error type is `Elapsed` which
|
//! // Check if the actual error type is `Elapsed` which
|
||||||
//! // `Timeout` returns
|
//! // `Timeout` returns
|
||||||
//! if error.is::<Elapsed>() {
|
//! if error.is::<Elapsed>() {
|
||||||
//! return (StatusCode::REQUEST_TIMEOUT, "Request took too long".into());
|
//! return Ok::<_, Infallible>((
|
||||||
|
//! StatusCode::REQUEST_TIMEOUT,
|
||||||
|
//! "Request took too long".into(),
|
||||||
|
//! ));
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! // If we encounter some error we don't handle return a generic
|
//! // If we encounter some error we don't handle return a generic
|
||||||
//! // error
|
//! // error
|
||||||
//! return (
|
//! return Ok::<_, Infallible>((
|
||||||
//! StatusCode::INTERNAL_SERVER_ERROR,
|
//! StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
//! // `Cow` lets us return either `&str` or `String`
|
//! // `Cow` lets us return either `&str` or `String`
|
||||||
//! Cow::from(format!("Unhandled internal error: {}", error)),
|
//! Cow::from(format!("Unhandled internal error: {}", error)),
|
||||||
//! );
|
//! ));
|
||||||
//! })),
|
//! })),
|
||||||
//! );
|
//! );
|
||||||
//!
|
//!
|
||||||
@@ -352,30 +406,10 @@
|
|||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! The closure passed to [`handle_error`](handler::Layered::handle_error) must
|
//! The closure passed to [`handle_error`](handler::Layered::handle_error) must
|
||||||
//! return something that implements [`IntoResponse`](response::IntoResponse).
|
//! return `Result<T, E>` where `T` implements
|
||||||
|
//! [`IntoResponse`](response::IntoResponse).
|
||||||
//!
|
//!
|
||||||
//! [`handle_error`](routing::Layered::handle_error) is also available on a
|
//! See [`routing::Layered::handle_error`] fo more details.
|
||||||
//! group of routes with middleware applied:
|
|
||||||
//!
|
|
||||||
//! ```rust,no_run
|
|
||||||
//! use axum::prelude::*;
|
|
||||||
//! use tower::{BoxError, timeout::TimeoutLayer};
|
|
||||||
//! use std::time::Duration;
|
|
||||||
//!
|
|
||||||
//! let app = route("/", get(handle))
|
|
||||||
//! .route("/foo", post(other_handle))
|
|
||||||
//! .layer(TimeoutLayer::new(Duration::from_secs(30)))
|
|
||||||
//! .handle_error(|error: BoxError| {
|
|
||||||
//! // ...
|
|
||||||
//! });
|
|
||||||
//!
|
|
||||||
//! async fn handle() {}
|
|
||||||
//!
|
|
||||||
//! async fn other_handle() {}
|
|
||||||
//! # async {
|
|
||||||
//! # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
|
||||||
//! # };
|
|
||||||
//! ```
|
|
||||||
//!
|
//!
|
||||||
//! ## Applying multiple middleware
|
//! ## Applying multiple middleware
|
||||||
//!
|
//!
|
||||||
@@ -383,14 +417,9 @@
|
|||||||
//!
|
//!
|
||||||
//! ```rust,no_run
|
//! ```rust,no_run
|
||||||
//! use axum::prelude::*;
|
//! use axum::prelude::*;
|
||||||
//! use tower::{
|
//! use tower::ServiceBuilder;
|
||||||
//! ServiceBuilder, BoxError,
|
|
||||||
//! load_shed::error::Overloaded,
|
|
||||||
//! timeout::error::Elapsed,
|
|
||||||
//! };
|
|
||||||
//! use tower_http::compression::CompressionLayer;
|
//! use tower_http::compression::CompressionLayer;
|
||||||
//! use std::{borrow::Cow, time::Duration};
|
//! use std::{borrow::Cow, time::Duration};
|
||||||
//! use http::StatusCode;
|
|
||||||
//!
|
//!
|
||||||
//! let middleware_stack = ServiceBuilder::new()
|
//! let middleware_stack = ServiceBuilder::new()
|
||||||
//! // Return an error after 30 seconds
|
//! // Return an error after 30 seconds
|
||||||
@@ -404,27 +433,7 @@
|
|||||||
//! .into_inner();
|
//! .into_inner();
|
||||||
//!
|
//!
|
||||||
//! let app = route("/", get(|_: Request<Body>| async { /* ... */ }))
|
//! let app = route("/", get(|_: Request<Body>| async { /* ... */ }))
|
||||||
//! .layer(middleware_stack)
|
//! .layer(middleware_stack);
|
||||||
//! .handle_error(|error: BoxError| {
|
|
||||||
//! if error.is::<Overloaded>() {
|
|
||||||
//! return (
|
|
||||||
//! StatusCode::SERVICE_UNAVAILABLE,
|
|
||||||
//! "Try again later".into(),
|
|
||||||
//! );
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! if error.is::<Elapsed>() {
|
|
||||||
//! return (
|
|
||||||
//! StatusCode::REQUEST_TIMEOUT,
|
|
||||||
//! "Request took too long".into(),
|
|
||||||
//! );
|
|
||||||
//! };
|
|
||||||
//!
|
|
||||||
//! return (
|
|
||||||
//! StatusCode::INTERNAL_SERVER_ERROR,
|
|
||||||
//! Cow::from(format!("Unhandled internal error: {}", error)),
|
|
||||||
//! );
|
|
||||||
//! });
|
|
||||||
//! # async {
|
//! # async {
|
||||||
//! # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
//! # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||||
//! # };
|
//! # };
|
||||||
@@ -466,10 +475,7 @@
|
|||||||
//! axum also supports routing to general [`Service`]s:
|
//! axum also supports routing to general [`Service`]s:
|
||||||
//!
|
//!
|
||||||
//! ```rust,no_run
|
//! ```rust,no_run
|
||||||
//! use axum::{
|
//! use axum::{service, prelude::*};
|
||||||
//! // `ServiceExt` adds `handle_error` to any `Service`
|
|
||||||
//! service::{self, ServiceExt}, prelude::*,
|
|
||||||
//! };
|
|
||||||
//! use tower_http::services::ServeFile;
|
//! use tower_http::services::ServeFile;
|
||||||
//! use http::Response;
|
//! use http::Response;
|
||||||
//! use std::convert::Infallible;
|
//! use std::convert::Infallible;
|
||||||
@@ -485,10 +491,7 @@
|
|||||||
//! ).route(
|
//! ).route(
|
||||||
//! // GET `/static/Cargo.toml` goes to a service from tower-http
|
//! // GET `/static/Cargo.toml` goes to a service from tower-http
|
||||||
//! "/static/Cargo.toml",
|
//! "/static/Cargo.toml",
|
||||||
//! service::get(
|
//! service::get(ServeFile::new("Cargo.toml"))
|
||||||
//! ServeFile::new("Cargo.toml")
|
|
||||||
//! .handle_error(|error: std::io::Error| { /* ... */ })
|
|
||||||
//! )
|
|
||||||
//! );
|
//! );
|
||||||
//! # async {
|
//! # async {
|
||||||
//! # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
//! # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||||
@@ -518,26 +521,12 @@
|
|||||||
//! # };
|
//! # };
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! [`nest`](routing::nest) can also be used to serve static files from a directory:
|
//! # Examples
|
||||||
//!
|
//!
|
||||||
//! ```rust,no_run
|
//! The axum repo contains [a number of examples][examples] that show how to put all the
|
||||||
//! use axum::{prelude::*, service::ServiceExt, routing::nest};
|
//! pieces togehter.
|
||||||
//! use tower_http::services::ServeDir;
|
|
||||||
//! use http::Response;
|
|
||||||
//! use tower::{service_fn, BoxError};
|
|
||||||
//!
|
//!
|
||||||
//! let app = nest(
|
//! # Feature flags
|
||||||
//! "/images",
|
|
||||||
//! ServeDir::new("public/images").handle_error(|error: std::io::Error| {
|
|
||||||
//! // ...
|
|
||||||
//! })
|
|
||||||
//! );
|
|
||||||
//! # async {
|
|
||||||
//! # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
|
||||||
//! # };
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! # Features
|
|
||||||
//!
|
//!
|
||||||
//! axum uses a set of [feature flags] to reduce the amount of compiled and
|
//! axum uses a set of [feature flags] to reduce the amount of compiled and
|
||||||
//! optional dependencies.
|
//! optional dependencies.
|
||||||
@@ -555,6 +544,7 @@
|
|||||||
//! [feature flags]: https://doc.rust-lang.org/cargo/reference/features.html#the-features-section
|
//! [feature flags]: https://doc.rust-lang.org/cargo/reference/features.html#the-features-section
|
||||||
//! [`IntoResponse`]: crate::response::IntoResponse
|
//! [`IntoResponse`]: crate::response::IntoResponse
|
||||||
//! [`Timeout`]: tower::timeout::Timeout
|
//! [`Timeout`]: tower::timeout::Timeout
|
||||||
|
//! [examples]: https://github.com/davidpdrsn/axum/tree/main/examples
|
||||||
|
|
||||||
#![doc(html_root_url = "https://docs.rs/tower-http/0.1.0")]
|
#![doc(html_root_url = "https://docs.rs/tower-http/0.1.0")]
|
||||||
#![warn(
|
#![warn(
|
||||||
|
|||||||
+42
-12
@@ -28,7 +28,7 @@ use tower::{
|
|||||||
};
|
};
|
||||||
use tower_http::map_response_body::MapResponseBodyLayer;
|
use tower_http::map_response_body::MapResponseBodyLayer;
|
||||||
|
|
||||||
/// A filter that matches one or more HTTP method.
|
/// A filter that matches one or more HTTP methods.
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub enum MethodFilter {
|
pub enum MethodFilter {
|
||||||
/// Match any method.
|
/// Match any method.
|
||||||
@@ -632,7 +632,7 @@ impl<S> Layered<S> {
|
|||||||
/// use axum::prelude::*;
|
/// use axum::prelude::*;
|
||||||
/// use http::StatusCode;
|
/// use http::StatusCode;
|
||||||
/// use tower::{BoxError, timeout::TimeoutLayer};
|
/// use tower::{BoxError, timeout::TimeoutLayer};
|
||||||
/// use std::time::Duration;
|
/// use std::{convert::Infallible, time::Duration};
|
||||||
///
|
///
|
||||||
/// async fn handler() { /* ... */ }
|
/// async fn handler() { /* ... */ }
|
||||||
///
|
///
|
||||||
@@ -643,18 +643,17 @@ impl<S> Layered<S> {
|
|||||||
/// // ...so we should handle that error
|
/// // ...so we should handle that error
|
||||||
/// let with_errors_handled = layered_app.handle_error(|error: BoxError| {
|
/// let with_errors_handled = layered_app.handle_error(|error: BoxError| {
|
||||||
/// if error.is::<tower::timeout::error::Elapsed>() {
|
/// if error.is::<tower::timeout::error::Elapsed>() {
|
||||||
/// (
|
/// Ok::<_, Infallible>((
|
||||||
/// StatusCode::REQUEST_TIMEOUT,
|
/// StatusCode::REQUEST_TIMEOUT,
|
||||||
/// "request took too long".to_string(),
|
/// "request took too long".to_string(),
|
||||||
/// )
|
/// ))
|
||||||
/// } else {
|
/// } else {
|
||||||
/// (
|
/// Ok::<_, Infallible>((
|
||||||
/// StatusCode::INTERNAL_SERVER_ERROR,
|
/// StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
/// format!("Unhandled internal error: {}", error),
|
/// format!("Unhandled internal error: {}", error),
|
||||||
/// )
|
/// ))
|
||||||
/// }
|
/// }
|
||||||
/// });
|
/// });
|
||||||
///
|
|
||||||
/// # async {
|
/// # async {
|
||||||
/// # hyper::Server::bind(&"".parse().unwrap())
|
/// # hyper::Server::bind(&"".parse().unwrap())
|
||||||
/// # .serve(with_errors_handled.into_make_service())
|
/// # .serve(with_errors_handled.into_make_service())
|
||||||
@@ -663,14 +662,46 @@ impl<S> Layered<S> {
|
|||||||
/// # };
|
/// # };
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// The closure can return any type that implements [`IntoResponse`].
|
/// The closure must return `Result<T, E>` where `T` implements [`IntoResponse`].
|
||||||
pub fn handle_error<F, ReqBody, ResBody, Res>(
|
///
|
||||||
|
/// You can also return `Err(_)` if you don't wish to handle the error:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use axum::prelude::*;
|
||||||
|
/// use http::StatusCode;
|
||||||
|
/// use tower::{BoxError, timeout::TimeoutLayer};
|
||||||
|
/// use std::time::Duration;
|
||||||
|
///
|
||||||
|
/// async fn handler() { /* ... */ }
|
||||||
|
///
|
||||||
|
/// let layered_app = route("/", get(handler))
|
||||||
|
/// .layer(TimeoutLayer::new(Duration::from_secs(30)));
|
||||||
|
///
|
||||||
|
/// let with_errors_handled = layered_app.handle_error(|error: BoxError| {
|
||||||
|
/// if error.is::<tower::timeout::error::Elapsed>() {
|
||||||
|
/// Ok((
|
||||||
|
/// StatusCode::REQUEST_TIMEOUT,
|
||||||
|
/// "request took too long".to_string(),
|
||||||
|
/// ))
|
||||||
|
/// } else {
|
||||||
|
/// // keep the error as is
|
||||||
|
/// Err(error)
|
||||||
|
/// }
|
||||||
|
/// });
|
||||||
|
/// # async {
|
||||||
|
/// # hyper::Server::bind(&"".parse().unwrap())
|
||||||
|
/// # .serve(with_errors_handled.into_make_service())
|
||||||
|
/// # .await
|
||||||
|
/// # .unwrap();
|
||||||
|
/// # };
|
||||||
|
/// ```
|
||||||
|
pub fn handle_error<F, ReqBody, ResBody, Res, E>(
|
||||||
self,
|
self,
|
||||||
f: F,
|
f: F,
|
||||||
) -> crate::service::HandleError<S, F, ReqBody>
|
) -> crate::service::HandleError<S, F, ReqBody>
|
||||||
where
|
where
|
||||||
S: Service<Request<ReqBody>, Response = Response<ResBody>> + Clone,
|
S: Service<Request<ReqBody>, Response = Response<ResBody>> + Clone,
|
||||||
F: FnOnce(S::Error) -> Res,
|
F: FnOnce(S::Error) -> Result<Res, E>,
|
||||||
Res: IntoResponse,
|
Res: IntoResponse,
|
||||||
ResBody: http_body::Body<Data = Bytes> + Send + Sync + 'static,
|
ResBody: http_body::Body<Data = Bytes> + Send + Sync + 'static,
|
||||||
ResBody::Error: Into<BoxError> + Send + Sync + 'static,
|
ResBody::Error: Into<BoxError> + Send + Sync + 'static,
|
||||||
@@ -757,8 +788,7 @@ where
|
|||||||
/// use tower_http::services::ServeDir;
|
/// use tower_http::services::ServeDir;
|
||||||
///
|
///
|
||||||
/// // Serves files inside the `public` directory at `GET /public/*`
|
/// // Serves files inside the `public` directory at `GET /public/*`
|
||||||
/// let serve_dir_service = ServeDir::new("public")
|
/// let serve_dir_service = ServeDir::new("public");
|
||||||
/// .handle_error(|error: std::io::Error| { /* ... */ });
|
|
||||||
///
|
///
|
||||||
/// let app = nest("/public", get(serve_dir_service));
|
/// let app = nest("/public", get(serve_dir_service));
|
||||||
/// # async {
|
/// # async {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ use futures_util::ready;
|
|||||||
use http::Response;
|
use http::Response;
|
||||||
use pin_project::pin_project;
|
use pin_project::pin_project;
|
||||||
use std::{
|
use std::{
|
||||||
convert::Infallible,
|
|
||||||
future::Future,
|
future::Future,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
@@ -25,15 +24,15 @@ pub struct HandleErrorFuture<Fut, F> {
|
|||||||
pub(super) f: Option<F>,
|
pub(super) f: Option<F>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Fut, F, E, B, Res> Future for HandleErrorFuture<Fut, F>
|
impl<Fut, F, E, E2, B, Res> Future for HandleErrorFuture<Fut, F>
|
||||||
where
|
where
|
||||||
Fut: Future<Output = Result<Response<B>, E>>,
|
Fut: Future<Output = Result<Response<B>, E>>,
|
||||||
F: FnOnce(E) -> Res,
|
F: FnOnce(E) -> Result<Res, E2>,
|
||||||
Res: IntoResponse,
|
Res: IntoResponse,
|
||||||
B: http_body::Body<Data = Bytes> + Send + Sync + 'static,
|
B: http_body::Body<Data = Bytes> + Send + Sync + 'static,
|
||||||
B::Error: Into<BoxError> + Send + Sync + 'static,
|
B::Error: Into<BoxError> + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
type Output = Result<Response<BoxBody>, Infallible>;
|
type Output = Result<Response<BoxBody>, E2>;
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
let this = self.project();
|
let this = self.project();
|
||||||
@@ -42,8 +41,10 @@ where
|
|||||||
Ok(res) => Ok(res.map(box_body)).into(),
|
Ok(res) => Ok(res.map(box_body)).into(),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let f = this.f.take().unwrap();
|
let f = this.f.take().unwrap();
|
||||||
let res = f(err).into_response();
|
match f(err) {
|
||||||
Ok(res.map(box_body)).into()
|
Ok(res) => Ok(res.into_response().map(box_body)).into(),
|
||||||
|
Err(err) => Err(err).into(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-9
@@ -507,16 +507,16 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, F, ReqBody, ResBody, Res> Service<Request<ReqBody>> for HandleError<S, F, ReqBody>
|
impl<S, F, ReqBody, ResBody, Res, E> Service<Request<ReqBody>> for HandleError<S, F, ReqBody>
|
||||||
where
|
where
|
||||||
S: Service<Request<ReqBody>, Response = Response<ResBody>> + Clone,
|
S: Service<Request<ReqBody>, Response = Response<ResBody>> + Clone,
|
||||||
F: FnOnce(S::Error) -> Res + Clone,
|
F: FnOnce(S::Error) -> Result<Res, E> + Clone,
|
||||||
Res: IntoResponse,
|
Res: IntoResponse,
|
||||||
ResBody: http_body::Body<Data = Bytes> + Send + Sync + 'static,
|
ResBody: http_body::Body<Data = Bytes> + Send + Sync + 'static,
|
||||||
ResBody::Error: Into<BoxError> + Send + Sync + 'static,
|
ResBody::Error: Into<BoxError> + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
type Response = Response<BoxBody>;
|
type Response = Response<BoxBody>;
|
||||||
type Error = Infallible;
|
type Error = E;
|
||||||
type Future = future::HandleErrorFuture<Oneshot<S, Request<ReqBody>>, F>;
|
type Future = future::HandleErrorFuture<Oneshot<S, Request<ReqBody>>, F>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
@@ -538,15 +538,16 @@ pub trait ServiceExt<ReqBody, ResBody>:
|
|||||||
/// Handle errors from a service.
|
/// Handle errors from a service.
|
||||||
///
|
///
|
||||||
/// `handle_error` takes a closure that will map errors from the service
|
/// `handle_error` takes a closure that will map errors from the service
|
||||||
/// into responses. The closure's return type must implement
|
/// into responses. The closure's return type must be `Result<T, E>` where
|
||||||
/// [`IntoResponse`].
|
/// `T` implements [`IntoIntoResponse`](crate::response::IntoResponse).
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// use axum::{service::{self, ServiceExt}, prelude::*};
|
/// use axum::{service::{self, ServiceExt}, prelude::*};
|
||||||
/// use http::Response;
|
/// use http::{Response, StatusCode};
|
||||||
/// use tower::{service_fn, BoxError};
|
/// use tower::{service_fn, BoxError};
|
||||||
|
/// use std::convert::Infallible;
|
||||||
///
|
///
|
||||||
/// // A service that might fail with `std::io::Error`
|
/// // A service that might fail with `std::io::Error`
|
||||||
/// let service = service_fn(|_: Request<Body>| async {
|
/// let service = service_fn(|_: Request<Body>| async {
|
||||||
@@ -557,7 +558,10 @@ pub trait ServiceExt<ReqBody, ResBody>:
|
|||||||
/// let app = route(
|
/// let app = route(
|
||||||
/// "/",
|
/// "/",
|
||||||
/// service.handle_error(|error: std::io::Error| {
|
/// service.handle_error(|error: std::io::Error| {
|
||||||
/// // Handle error by returning something that implements `IntoResponse`
|
/// Ok::<_, Infallible>((
|
||||||
|
/// StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
/// error.to_string(),
|
||||||
|
/// ))
|
||||||
/// }),
|
/// }),
|
||||||
/// );
|
/// );
|
||||||
/// #
|
/// #
|
||||||
@@ -565,10 +569,14 @@ pub trait ServiceExt<ReqBody, ResBody>:
|
|||||||
/// # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
/// # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||||
/// # };
|
/// # };
|
||||||
/// ```
|
/// ```
|
||||||
fn handle_error<F, Res>(self, f: F) -> HandleError<Self, F, ReqBody>
|
///
|
||||||
|
/// It works similarly to [`routing::Layered::handle_error`]. See that for more details.
|
||||||
|
///
|
||||||
|
/// [`routing::Layered::handle_error`]: crate::routing::Layered::handle_error
|
||||||
|
fn handle_error<F, Res, E>(self, f: F) -> HandleError<Self, F, ReqBody>
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
F: FnOnce(Self::Error) -> Res,
|
F: FnOnce(Self::Error) -> Result<Res, E>,
|
||||||
Res: IntoResponse,
|
Res: IntoResponse,
|
||||||
ResBody: http_body::Body<Data = Bytes> + Send + Sync + 'static,
|
ResBody: http_body::Body<Data = Bytes> + Send + Sync + 'static,
|
||||||
ResBody::Error: Into<BoxError> + Send + Sync + 'static,
|
ResBody::Error: Into<BoxError> + Send + Sync + 'static,
|
||||||
|
|||||||
+7
-4
@@ -8,6 +8,7 @@ use hyper::{Body, Server};
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::{
|
use std::{
|
||||||
|
convert::Infallible,
|
||||||
net::{SocketAddr, TcpListener},
|
net::{SocketAddr, TcpListener},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
@@ -340,7 +341,7 @@ async fn service_handlers() {
|
|||||||
service_fn(|req: Request<Body>| async move {
|
service_fn(|req: Request<Body>| async move {
|
||||||
Ok::<_, BoxError>(Response::new(req.into_body()))
|
Ok::<_, BoxError>(Response::new(req.into_body()))
|
||||||
})
|
})
|
||||||
.handle_error(|_error: BoxError| StatusCode::INTERNAL_SERVER_ERROR),
|
.handle_error(|_error: BoxError| Ok(StatusCode::INTERNAL_SERVER_ERROR)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
@@ -348,7 +349,7 @@ async fn service_handlers() {
|
|||||||
service::on(
|
service::on(
|
||||||
MethodFilter::Get,
|
MethodFilter::Get,
|
||||||
ServeFile::new("Cargo.toml").handle_error(|error: std::io::Error| {
|
ServeFile::new("Cargo.toml").handle_error(|error: std::io::Error| {
|
||||||
(StatusCode::INTERNAL_SERVER_ERROR, error.to_string())
|
Ok::<_, Infallible>((StatusCode::INTERNAL_SERVER_ERROR, error.to_string()))
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -485,7 +486,9 @@ async fn handling_errors_from_layered_single_routes() {
|
|||||||
.layer(TraceLayer::new_for_http())
|
.layer(TraceLayer::new_for_http())
|
||||||
.into_inner(),
|
.into_inner(),
|
||||||
)
|
)
|
||||||
.handle_error(|_error: BoxError| StatusCode::INTERNAL_SERVER_ERROR)),
|
.handle_error(|_error: BoxError| {
|
||||||
|
Ok::<_, Infallible>(StatusCode::INTERNAL_SERVER_ERROR)
|
||||||
|
})),
|
||||||
);
|
);
|
||||||
|
|
||||||
let addr = run_in_background(app).await;
|
let addr = run_in_background(app).await;
|
||||||
@@ -508,7 +511,7 @@ async fn layer_on_whole_router() {
|
|||||||
.timeout(Duration::from_millis(100))
|
.timeout(Duration::from_millis(100))
|
||||||
.into_inner(),
|
.into_inner(),
|
||||||
)
|
)
|
||||||
.handle_error(|_err: BoxError| StatusCode::INTERNAL_SERVER_ERROR);
|
.handle_error(|_err: BoxError| Ok::<_, Infallible>(StatusCode::INTERNAL_SERVER_ERROR));
|
||||||
|
|
||||||
let addr = run_in_background(app).await;
|
let addr = run_in_background(app).await;
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -56,7 +56,7 @@ where
|
|||||||
crate::service::get::<_, B>(svc)
|
crate::service::get::<_, B>(svc)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [`Service`] that ugprades connections to websockets and spawns a task to
|
/// [`Service`] that upgrades connections to websockets and spawns a task to
|
||||||
/// handle the stream.
|
/// handle the stream.
|
||||||
///
|
///
|
||||||
/// Created with [`ws`].
|
/// Created with [`ws`].
|
||||||
|
|||||||
Reference in New Issue
Block a user