mirror of
https://github.com/tokio-rs/axum.git
synced 2026-08-30 00:00:32 +02:00
Rework docs (#437)
This reworks axum's docs in an attempt to make things easier to find. Previously I wasn't a fan of those docs for the same topic were spread across the root module docs and more specific places like types and methods. This changes it such that the root module docs only gives a high level introduction to a topic, perhaps with a small example, and then link to other places where all the details are. This means `Router` is now the single place to learn about routing, and etc for the topics like handlers and error handling.
This commit is contained in:
+75
-3
@@ -1,10 +1,77 @@
|
||||
//! Async functions that can be used to handle requests.
|
||||
//!
|
||||
#![doc = include_str!("../docs/handlers_intro.md")]
|
||||
//!
|
||||
//! Some examples of handlers:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use bytes::Bytes;
|
||||
//! use http::StatusCode;
|
||||
//!
|
||||
//! // Handler that immediately returns an empty `200 OK` response.
|
||||
//! async fn unit_handler() {}
|
||||
//!
|
||||
//! // Handler that immediately returns an empty `200 OK` response with a plain
|
||||
//! // text body.
|
||||
//! async fn string_handler() -> String {
|
||||
//! "Hello, World!".to_string()
|
||||
//! }
|
||||
//!
|
||||
//! // Handler that buffers the request body and returns it.
|
||||
//! //
|
||||
//! // This works because `Bytes` implements `FromRequest`
|
||||
//! // and therefore can be used as an extractor.
|
||||
//! //
|
||||
//! // `String` and `StatusCode` both implement `IntoResponse` and
|
||||
//! // therefore `Result<String, StatusCode>` also implements `IntoResponse`
|
||||
//! async fn echo(body: Bytes) -> Result<String, StatusCode> {
|
||||
//! if let Ok(string) = String::from_utf8(body.to_vec()) {
|
||||
//! Ok(string)
|
||||
//! } else {
|
||||
//! Err(StatusCode::BAD_REQUEST)
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Debugging handler type errors
|
||||
//!
|
||||
//! For a function to used as a handler it must implement the [`Handler`] trait.
|
||||
//! axum provides blanket implementations for functions that:
|
||||
//!
|
||||
//! - Are `async fn`s.
|
||||
//! - Take no more than 16 arguments that all implement [`FromRequest`].
|
||||
//! - Returns something that implements [`IntoResponse`].
|
||||
//! - If a closure is used it must implement `Clone + Send + Sync` and be
|
||||
//! `'static`.
|
||||
//! - Returns a future that is `Send`. The most common way to accidentally make a
|
||||
//! future `!Send` is to hold a `!Send` type across an await.
|
||||
//!
|
||||
//! Unfortunately Rust gives poor error messages if you try to use a function
|
||||
//! that doesn't quite match what's required by [`Handler`].
|
||||
//!
|
||||
//! You might get an error like this:
|
||||
//!
|
||||
//! ```not_rust
|
||||
//! error[E0277]: the trait bound `fn(bool) -> impl Future {handler}: Handler<_, _>` is not satisfied
|
||||
//! --> src/main.rs:13:44
|
||||
//! |
|
||||
//! 13 | let app = Router::new().route("/", get(handler));
|
||||
//! | ^^^^^^^ the trait `Handler<_, _>` is not implemented for `fn(bool) -> impl Future {handler}`
|
||||
//! |
|
||||
//! ::: axum/src/handler/mod.rs:116:8
|
||||
//! |
|
||||
//! 116 | H: Handler<B, T>,
|
||||
//! | ------------- required by this bound in `axum::routing::get`
|
||||
//! ```
|
||||
//!
|
||||
//! This error doesn't tell you _why_ your function doesn't implement
|
||||
//! [`Handler`]. It's possible to improve the error with the [`debug_handler`]
|
||||
//! proc-macro from the [axum-debug] crate.
|
||||
|
||||
use crate::{
|
||||
body::{box_body, BoxBody},
|
||||
extract::{FromRequest, RequestParts},
|
||||
response::IntoResponse,
|
||||
routing::{MethodNotAllowed, MethodRouter},
|
||||
BoxError,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
@@ -55,6 +122,10 @@ pub trait Handler<B, T>: Clone + Send + Sized + 'static {
|
||||
/// Note this differs from [`routing::Router::layer`](crate::routing::Router::layer)
|
||||
/// which adds a middleware to a group of routes.
|
||||
///
|
||||
/// If you're applying middleware that produces errors you have to handle the errors
|
||||
/// so they're converted into responses. You can learn more about doing that
|
||||
/// [here](crate::error_handling).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Adding the [`tower::limit::ConcurrencyLimit`] middleware to a handler
|
||||
@@ -78,9 +149,9 @@ pub trait Handler<B, T>: Clone + Send + Sized + 'static {
|
||||
/// ```
|
||||
fn layer<L>(self, layer: L) -> Layered<L::Service, T>
|
||||
where
|
||||
L: Layer<MethodRouter<Self, B, T, MethodNotAllowed>>,
|
||||
L: Layer<IntoService<Self, B, T>>,
|
||||
{
|
||||
Layered::new(layer.layer(crate::routing::any(self)))
|
||||
Layered::new(layer.layer(self.into_service()))
|
||||
}
|
||||
|
||||
/// Convert the handler into a [`Service`].
|
||||
@@ -239,6 +310,7 @@ impl<S, T> Layered<S, T> {
|
||||
|
||||
#[test]
|
||||
fn traits() {
|
||||
use crate::routing::MethodRouter;
|
||||
use crate::tests::*;
|
||||
assert_send::<MethodRouter<(), NotSendSync, NotSendSync, ()>>();
|
||||
assert_sync::<MethodRouter<(), NotSendSync, NotSendSync, ()>>();
|
||||
|
||||
Reference in New Issue
Block a user