1Error handling model and utilities 2 3# Table of contents 4 5- [axum's error handling model](#axums-error-handling-model) 6- [Routing to fallible services](#routing-to-fallible-services) 7- [Applying fallible middleware](#applying-fallible-middleware) 8- [Running extractors for error handling](#running-extractors-for-error-handling) 9 10# axum's error handling model 11 12axum is based on [`tower::Service`] which bundles errors through its associated 13`Error` type. If you have a [`Service`] that produces an error and that error 14makes it all the way up to hyper, the connection will be terminated _without_ 15sending a response. This is generally not desirable so axum makes sure you 16always produce a response by relying on the type system. 17 18axum does this by requiring all services have [`Infallible`] as their error 19type. `Infallible` is the error type for errors that can never happen. 20 21This means if you define a handler like: 22 23```rust 24use axum::http::StatusCode; 25 26async fn handler() -> Result<String, StatusCode> { 27 # todo!() 28 // ... 29} 30``` 31 32While it looks like it might fail with a `StatusCode` this actually isn't an 33"error". If this handler returns `Err(some_status_code)` that will still be 34converted into a [`Response`] and sent back to the client. This is done 35through `StatusCode`'s [`IntoResponse`] implementation. 36 37It doesn't matter whether you return `Err(StatusCode::NOT_FOUND)` or 38`Err(StatusCode::INTERNAL_SERVER_ERROR)`. These are not considered errors in 39axum. 40 41Instead of a direct `StatusCode`, it makes sense to use intermediate error type 42that can ultimately be converted to `Response`. This allows using `?` operator 43in handlers. See those examples: 44 45* [`anyhow-error-response`][anyhow] for generic boxed errors 46* [`error-handling-and-dependency-injection`][ehdi] for application-specific detailed errors 47 48[anyhow]: https://github.com/tokio-rs/axum/blob/main/examples/anyhow-error-response/src/main.rs 49[ehdi]: https://github.com/tokio-rs/axum/blob/main/examples/error-handling-and-dependency-injection/src/main.rs 50 51This also applies to extractors. If an extractor doesn't match the request the 52request will be rejected and a response will be returned without calling your 53handler. See [`extract`](crate::extract) to learn more about handling extractor 54failures. 55 56# Routing to fallible services 57 58You generally don't have to think about errors if you're only using async 59functions as handlers. However if you're embedding general `Service`s or 60applying middleware, which might produce errors you have to tell axum how to 61convert those errors into responses. 62 63```rust 64use axum::{ 65 Router, 66 body::Body, 67 http::{Request, Response, StatusCode}, 68 error_handling::HandleError, 69}; 70 71async fn thing_that_might_fail() -> Result<(), anyhow::Error> { 72 # Ok(()) 73 // ... 74} 75 76// this service might fail with `anyhow::Error` 77let some_fallible_service = tower::service_fn(|_req| async { 78 thing_that_might_fail().await?; 79 Ok::<_, anyhow::Error>(Response::new(Body::empty())) 80}); 81 82let app = Router::new().route_service( 83 "/", 84 // we cannot route to `some_fallible_service` directly since it might fail. 85 // we have to use `handle_error` which converts its errors into responses 86 // and changes its error type from `anyhow::Error` to `Infallible`. 87 HandleError::new(some_fallible_service, handle_anyhow_error), 88); 89 90// handle errors by converting them into something that implements 91// `IntoResponse` 92async fn handle_anyhow_error(err: anyhow::Error) -> (StatusCode, String) { 93 ( 94 StatusCode::INTERNAL_SERVER_ERROR, 95 format!("Something went wrong: {}", err), 96 ) 97} 98# async { 99# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); 100# }; 101``` 102 103# Applying fallible middleware 104 105Similarly axum requires you to handle errors from middleware. That is done with 106[`HandleErrorLayer`]: 107 108```rust 109use axum::{ 110 Router, 111 BoxError, 112 routing::get, 113 http::StatusCode, 114 error_handling::HandleErrorLayer, 115}; 116use std::time::Duration; 117use tower::ServiceBuilder; 118 119let app = Router::new() 120 .route("/", get(|| async {})) 121 .layer( 122 ServiceBuilder::new() 123 // `timeout` will produce an error if the handler takes 124 // too long so we must handle those 125 .layer(HandleErrorLayer::new(handle_timeout_error)) 126 .timeout(Duration::from_secs(30)) 127 ); 128 129async fn handle_timeout_error(err: BoxError) -> (StatusCode, String) { 130 if err.is::<tower::timeout::error::Elapsed>() { 131 ( 132 StatusCode::REQUEST_TIMEOUT, 133 "Request took too long".to_string(), 134 ) 135 } else { 136 ( 137 StatusCode::INTERNAL_SERVER_ERROR, 138 format!("Unhandled internal error: {}", err), 139 ) 140 } 141} 142# async { 143# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); 144# }; 145``` 146 147# Running extractors for error handling 148 149`HandleErrorLayer` also supports running extractors: 150 151```rust 152use axum::{ 153 Router, 154 BoxError, 155 routing::get, 156 http::{StatusCode, Method, Uri}, 157 error_handling::HandleErrorLayer, 158}; 159use std::time::Duration; 160use tower::ServiceBuilder; 161 162let app = Router::new() 163 .route("/", get(|| async {})) 164 .layer( 165 ServiceBuilder::new() 166 // `timeout` will produce an error if the handler takes 167 // too long so we must handle those 168 .layer(HandleErrorLayer::new(handle_timeout_error)) 169 .timeout(Duration::from_secs(30)) 170 ); 171 172async fn handle_timeout_error( 173 // `Method` and `Uri` are extractors so they can be used here 174 method: Method, 175 uri: Uri, 176 // the last argument must be the error itself 177 err: BoxError, 178) -> (StatusCode, String) { 179 ( 180 StatusCode::INTERNAL_SERVER_ERROR, 181 format!("`{} {}` failed with {}", method, uri, err), 182 ) 183} 184# async { 185# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); 186# }; 187``` 188 189[`tower::Service`]: `tower::Service` 190[`Infallible`]: std::convert::Infallible 191[`Response`]: crate::response::Response 192[`IntoResponse`]: crate::response::IntoResponse 193