, headers: HeaderMap, // `State` is also an extractor so it needs to be before `body` State(state): State, // `String` consumes the request body and thus must be the last extractor body: String, ) { // ... } # # let _: axum::routing::MethodRouter = 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, ) { // ... } # # 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, 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>) { 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` 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, JsonRejection>, ) -> Result, (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`, // 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(err: E) -> (StatusCode, String) where E: Error + 'static, { if let Some(err) = find_error_source::>(&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::() { 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::::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 FromRequestParts for ExtractUserAgent where S: Send + Sync, { type Rejection = (StatusCode, &'static str); async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { 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 FromRequest for ValidatedBody where Bytes: FromRequest, S: Send + Sync, { type Rejection = Response; async fn from_request(req: Request, state: &S) -> Result { 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 FromRequest for MyExtractor where S: Send + Sync, { type Rejection = Infallible; async fn from_request(req: Request, state: &S) -> Result { // ... # todo!() } } // and `FromRequestParts` impl FromRequestParts for MyExtractor where S: Send + Sync, { type Rejection = Infallible; async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { // ... # 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 FromRequestParts for AuthenticatedUser where S: Send + Sync, { type Rejection = Response; async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { // 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::>() .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 to 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 { extractor: E, duration: Duration, } // we must implement both `FromRequestParts` impl FromRequestParts for Timing where S: Send + Sync, T: FromRequestParts, { type Rejection = T::Rejection; async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { let start = Instant::now(); let extractor = T::from_request_parts(parts, state).await?; let duration = start.elapsed(); Ok(Timing { extractor, duration, }) } } // and `FromRequest` impl FromRequest for Timing where S: Send + Sync, T: FromRequest, { type Rejection = T::Rejection; async fn from_request(req: Request, state: &S) -> Result { 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, // this uses the `FromRequest` impl _: Timing, ) {} # 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