1Types and traits for extracting data from requests.
2
3# Table of contents
4
5- [Intro](#intro)
6- [Common extractors](#common-extractors)
7- [Applying multiple extractors](#applying-multiple-extractors)
8- [The order of extractors](#the-order-of-extractors)
9- [Optional extractors](#optional-extractors)
10- [Customizing extractor responses](#customizing-extractor-responses)
11- [Accessing inner errors](#accessing-inner-errors)
12- [Defining custom extractors](#defining-custom-extractors)
13- [Accessing other extractors in `FromRequest` or `FromRequestParts` implementations](#accessing-other-extractors-in-fromrequest-or-fromrequestparts-implementations)
14- [Request body limits](#request-body-limits)
15- [Request body extractors](#request-body-extractors)
16- [Running extractors from middleware](#running-extractors-from-middleware)
17- [Wrapping extractors](#wrapping-extractors)
18- [Logging rejections](#logging-rejections)
19
20# Intro
21
22A handler function is an async function that takes any number of
23"extractors" as arguments. An extractor is a type that implements
24[`FromRequest`](crate::extract::FromRequest)
25or [`FromRequestParts`](crate::extract::FromRequestParts).
26
27For example, [`Json`] is an extractor that consumes the request body and
28deserializes it as JSON into some target type:
29
30```rust,no_run
31use axum::{
32    extract::Json,
33    routing::post,
34    handler::Handler,
35    Router,
36};
37use serde::Deserialize;
38
39#[derive(Deserialize)]
40struct CreateUser {
41    email: String,
42    password: String,
43}
44
45async fn create_user(Json(payload): Json<CreateUser>) {
46    // ...
47}
48
49let app = Router::new().route("/users", post(create_user));
50# async {
51# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
52# };
53```
54
55# Common extractors
56
57Some commonly used extractors are:
58
59```rust,no_run
60use axum::{
61    extract::{Json, TypedHeader, Path, Extension, Query},
62    routing::post,
63    headers::UserAgent,
64    http::{Request, header::HeaderMap},
65    body::{Bytes, Body},
66    Router,
67};
68use serde_json::Value;
69use std::collections::HashMap;
70
71// `Path` gives you the path parameters and deserializes them. See its docs for
72// more details
73async fn path(Path(user_id): Path<u32>) {}
74
75// `Query` gives you the query parameters and deserializes them.
76async fn query(Query(params): Query<HashMap<String, String>>) {}
77
78// `HeaderMap` gives you all the headers
79async fn headers(headers: HeaderMap) {}
80
81// `TypedHeader` can be used to extract a single header
82// note this requires you've enabled axum's `headers` feature
83async fn user_agent(TypedHeader(user_agent): TypedHeader<UserAgent>) {}
84
85// `String` consumes the request body and ensures it is valid utf-8
86async fn string(body: String) {}
87
88// `Bytes` gives you the raw request body
89async fn bytes(body: Bytes) {}
90
91// We've already seen `Json` for parsing the request body as json
92async fn json(Json(payload): Json<Value>) {}
93
94// `Request` gives you the whole request for maximum control
95async fn request(request: Request<Body>) {}
96
97// `Extension` extracts data from "request extensions"
98// This is commonly used to share state with handlers
99async fn extension(Extension(state): Extension<State>) {}
100
101#[derive(Clone)]
102struct State { /* ... */ }
103
104let app = Router::new()
105    .route("/path/:user_id", post(path))
106    .route("/query", post(query))
107    .route("/user_agent", post(user_agent))
108    .route("/headers", post(headers))
109    .route("/string", post(string))
110    .route("/bytes", post(bytes))
111    .route("/json", post(json))
112    .route("/request", post(request))
113    .route("/extension", post(extension));
114# async {
115# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
116# };
117```
118
119# Applying multiple extractors
120
121You can also apply multiple extractors:
122
123```rust,no_run
124use axum::{
125    extract::{Path, Query},
126    routing::get,
127    Router,
128};
129use uuid::Uuid;
130use serde::Deserialize;
131
132let app = Router::new().route("/users/:id/things", get(get_user_things));
133
134#[derive(Deserialize)]
135struct Pagination {
136    page: usize,
137    per_page: usize,
138}
139
140impl Default for Pagination {
141    fn default() -> Self {
142        Self { page: 1, per_page: 30 }
143    }
144}
145
146async fn get_user_things(
147    Path(user_id): Path<Uuid>,
148    pagination: Option<Query<Pagination>>,
149) {
150    let Query(pagination) = pagination.unwrap_or_default();
151
152    // ...
153}
154# async {
155# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
156# };
157```
158
159# The order of extractors
160
161Extractors always run in the order of the function parameters that is from
162left to right.
163
164The request body is an asynchronous stream that can only be consumed once.
165Therefore you can only have one extractor that consumes the request body. axum
166enforces this by requiring such extractors to be the _last_ argument your
167handler takes.
168
169For example
170
171```rust
172use axum::{extract::State, http::{Method, HeaderMap}};
173#
174# #[derive(Clone)]
175# struct AppState {
176# }
177
178async fn handler(
179    // `Method` and `HeaderMap` don't consume the request body so they can
180    // put anywhere in the argument list (but before `body`)
181    method: Method,
182    headers: HeaderMap,
183    // `State` is also an extractor so it needs to be before `body`
184    State(state): State<AppState>,
185    // `String` consumes the request body and thus must be the last extractor
186    body: String,
187) {
188    // ...
189}
190#
191# let _: axum::routing::MethodRouter<AppState, String> = axum::routing::get(handler);
192```
193
194We get a compile error if `String` isn't the last extractor:
195
196```rust,compile_fail
197use axum::http::Method;
198
199async fn handler(
200    // this doesn't work since `String` must be the last argument
201    body: String,
202    method: Method,
203) {
204    // ...
205}
206#
207# let _: axum::routing::MethodRouter = axum::routing::get(handler);
208```
209
210This also means you cannot consume the request body twice:
211
212```rust,compile_fail
213use axum::Json;
214use serde::Deserialize;
215
216#[derive(Deserialize)]
217struct Payload {}
218
219async fn handler(
220    // `String` and `Json` both consume the request body
221    // so they cannot both be used
222    string_body: String,
223    json_body: Json<Payload>,
224) {
225    // ...
226}
227#
228# let _: axum::routing::MethodRouter = axum::routing::get(handler);
229```
230
231axum enforces this by requiring the last extractor implements [`FromRequest`]
232and all others implement [`FromRequestParts`].
233
234# Optional extractors
235
236All extractors defined in axum will reject the request if it doesn't match.
237If you wish to make an extractor optional you can wrap it in `Option`:
238
239```rust,no_run
240use axum::{
241    extract::Json,
242    routing::post,
243    Router,
244};
245use serde_json::Value;
246
247async fn create_user(payload: Option<Json<Value>>) {
248    if let Some(payload) = payload {
249        // We got a valid JSON payload
250    } else {
251        // Payload wasn't valid JSON
252    }
253}
254
255let app = Router::new().route("/users", post(create_user));
256# async {
257# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
258# };
259```
260
261Wrapping extractors in `Result` makes them optional and gives you the reason
262the extraction failed:
263
264```rust,no_run
265use axum::{
266    extract::{Json, rejection::JsonRejection},
267    routing::post,
268    Router,
269};
270use serde_json::Value;
271
272async fn create_user(payload: Result<Json<Value>, JsonRejection>) {
273    match payload {
274        Ok(payload) => {
275            // We got a valid JSON payload
276        }
277        Err(JsonRejection::MissingJsonContentType(_)) => {
278            // Request didn't have `Content-Type: application/json`
279            // header
280        }
281        Err(JsonRejection::JsonDataError(_)) => {
282            // Couldn't deserialize the body into the target type
283        }
284        Err(JsonRejection::JsonSyntaxError(_)) => {
285            // Syntax error in the body
286        }
287        Err(JsonRejection::BytesRejection(_)) => {
288            // Failed to extract the request body
289        }
290        Err(_) => {
291            // `JsonRejection` is marked `#[non_exhaustive]` so match must
292            // include a catch-all case.
293        }
294    }
295}
296
297let app = Router::new().route("/users", post(create_user));
298# async {
299# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
300# };
301```
302
303# Customizing extractor responses
304
305If an extractor fails it will return a response with the error and your
306handler will not be called. To customize the error response you have a two
307options:
308
3091. Use `Result<T, T::Rejection>` as your extractor like shown in ["Optional
310   extractors"](#optional-extractors). This works well if you're only using
311   the extractor in a single handler.
3122. Create your own extractor that in its [`FromRequest`] implemention calls
313   one of axum's built in extractors but returns a different response for
314   rejections. See the [customize-extractor-error] example for more details.
315
316# Accessing inner errors
317
318axum's built-in extractors don't directly expose the inner error. This gives us
319more flexibility and allows us to change internal implementations without
320breaking the public API.
321
322For example that means while [`Json`] is implemented using [`serde_json`] it
323doesn't directly expose the [`serde_json::Error`] thats contained in
324[`JsonRejection::JsonDataError`]. However it is still possible to access via
325methods from [`std::error::Error`]:
326
327```rust
328use std::error::Error;
329use axum::{
330    extract::{Json, rejection::JsonRejection},
331    response::IntoResponse,
332    http::StatusCode,
333};
334use serde_json::{json, Value};
335
336async fn handler(
337    result: Result<Json<Value>, JsonRejection>,
338) -> Result<Json<Value>, (StatusCode, String)> {
339    match result {
340        // if the client sent valid JSON then we're good
341        Ok(Json(payload)) => Ok(Json(json!({ "payload": payload }))),
342
343        Err(err) => match err {
344            JsonRejection::JsonDataError(err) => {
345                Err(serde_json_error_response(err))
346            }
347            JsonRejection::JsonSyntaxError(err) => {
348                Err(serde_json_error_response(err))
349            }
350            // handle other rejections from the `Json` extractor
351            JsonRejection::MissingJsonContentType(_) => Err((
352                StatusCode::BAD_REQUEST,
353                "Missing `Content-Type: application/json` header".to_string(),
354            )),
355            JsonRejection::BytesRejection(_) => Err((
356                StatusCode::INTERNAL_SERVER_ERROR,
357                "Failed to buffer request body".to_string(),
358            )),
359            // we must provide a catch-all case since `JsonRejection` is marked
360            // `#[non_exhaustive]`
361            _ => Err((
362                StatusCode::INTERNAL_SERVER_ERROR,
363                "Unknown error".to_string(),
364            )),
365        },
366    }
367}
368
369// attempt to extract the inner `serde_path_to_error::Error<serde_json::Error>`,
370// if that succeeds we can provide a more specific error.
371//
372// `Json` uses `serde_path_to_error` so the error will be wrapped in `serde_path_to_error::Error`.
373fn serde_json_error_response<E>(err: E) -> (StatusCode, String)
374where
375    E: Error + 'static,
376{
377    if let Some(err) = find_error_source::<serde_path_to_error::Error<serde_json::Error>>(&err) {
378        let serde_json_err = err.inner();
379        (
380            StatusCode::BAD_REQUEST,
381            format!(
382                "Invalid JSON at line {} column {}",
383                serde_json_err.line(),
384                serde_json_err.column()
385            ),
386        )
387    } else {
388        (StatusCode::BAD_REQUEST, "Unknown error".to_string())
389    }
390}
391
392// attempt to downcast `err` into a `T` and if that fails recursively try and
393// downcast `err`'s source
394fn find_error_source<'a, T>(err: &'a (dyn Error + 'static)) -> Option<&'a T>
395where
396    T: Error + 'static,
397{
398    if let Some(err) = err.downcast_ref::<T>() {
399        Some(err)
400    } else if let Some(source) = err.source() {
401        find_error_source(source)
402    } else {
403        None
404    }
405}
406#
407# #[tokio::main]
408# async fn main() {
409#     use axum::extract::FromRequest;
410#
411#     let req = axum::http::Request::builder()
412#         .header("content-type", "application/json")
413#         .body(axum::body::Body::from("{"))
414#         .unwrap();
415#
416#     let err = match Json::<serde_json::Value>::from_request(req, &()).await.unwrap_err() {
417#         JsonRejection::JsonSyntaxError(err) => err,
418#         _ => panic!(),
419#     };
420#
421#     let (_, body) = serde_json_error_response(err);
422#     assert_eq!(body, "Invalid JSON at line 1 column 1");
423# }
424```
425
426Note that while this approach works it might break in the future if axum changes
427its implementation to use a different error type internally. Such changes might
428happen without major breaking versions.
429
430# Defining custom extractors
431
432You can also define your own extractors by implementing either
433[`FromRequestParts`] or [`FromRequest`].
434
435## Implementing `FromRequestParts`
436
437Implement `FromRequestParts` if your extractor doesn't need access to the
438request body:
439
440```rust,no_run
441use axum::{
442    async_trait,
443    extract::FromRequestParts,
444    routing::get,
445    Router,
446    http::{
447        StatusCode,
448        header::{HeaderValue, USER_AGENT},
449        request::Parts,
450    },
451};
452
453struct ExtractUserAgent(HeaderValue);
454
455#[async_trait]
456impl<S> FromRequestParts<S> for ExtractUserAgent
457where
458    S: Send + Sync,
459{
460    type Rejection = (StatusCode, &'static str);
461
462    async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
463        if let Some(user_agent) = parts.headers.get(USER_AGENT) {
464            Ok(ExtractUserAgent(user_agent.clone()))
465        } else {
466            Err((StatusCode::BAD_REQUEST, "`User-Agent` header is missing"))
467        }
468    }
469}
470
471async fn handler(ExtractUserAgent(user_agent): ExtractUserAgent) {
472    // ...
473}
474
475let app = Router::new().route("/foo", get(handler));
476# async {
477# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
478# };
479```
480
481## Implementing `FromRequest`
482
483If your extractor needs to consume the request body you must implement [`FromRequest`]
484
485```rust,no_run
486use axum::{
487    async_trait,
488    extract::FromRequest,
489    response::{Response, IntoResponse},
490    body::Bytes,
491    routing::get,
492    Router,
493    http::{
494        StatusCode,
495        header::{HeaderValue, USER_AGENT},
496        Request,
497    },
498};
499
500struct ValidatedBody(Bytes);
501
502#[async_trait]
503impl<S, B> FromRequest<S, B> for ValidatedBody
504where
505    Bytes: FromRequest<S, B>,
506    B: Send + 'static,
507    S: Send + Sync,
508{
509    type Rejection = Response;
510
511    async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
512        let body = Bytes::from_request(req, state)
513            .await
514            .map_err(IntoResponse::into_response)?;
515
516        // do validation...
517
518        Ok(Self(body))
519    }
520}
521
522async fn handler(ValidatedBody(body): ValidatedBody) {
523    // ...
524}
525
526let app = Router::new().route("/foo", get(handler));
527# async {
528# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
529# };
530```
531
532## Cannot implement both `FromRequest` and `FromRequestParts`
533
534Note that you will make your extractor unusable by implementing both
535`FromRequest` and `FromRequestParts` directly for the same type, unless it is
536wrapping another extractor:
537
538```rust,compile_fail
539use axum::{
540    Router,
541    routing::get,
542    extract::{FromRequest, FromRequestParts},
543    http::{Request, request::Parts},
544    async_trait,
545};
546use std::convert::Infallible;
547
548// Some extractor that doesn't wrap another extractor
549struct MyExtractor;
550
551// `MyExtractor` implements both `FromRequest`
552#[async_trait]
553impl<S, B> FromRequest<S, B> for MyExtractor
554where
555    S: Send + Sync,
556    B: Send + 'static,
557{
558    type Rejection = Infallible;
559
560    async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
561        // ...
562        # todo!()
563    }
564}
565
566// and `FromRequestParts`
567#[async_trait]
568impl<S> FromRequestParts<S> for MyExtractor
569where
570    S: Send + Sync,
571{
572    type Rejection = Infallible;
573
574    async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
575        // ...
576        # todo!()
577    }
578}
579
580let app = Router::new().route(
581    "/",
582    // This fails when we go to actually use `MyExtractor` in a handler function.
583    // This is due to a limit in Rust's type system.
584    //
585    // The workaround is to implement either `FromRequest` or `FromRequestParts`
586    // but not both, if your extractor doesn't wrap another extractor.
587    //
588    // See "Wrapping extractors" for how to wrap other extractors.
589    get(|_: MyExtractor| async {}),
590);
591# let _: Router = app;
592```
593
594# Accessing other extractors in `FromRequest` or `FromRequestParts` implementations
595
596When defining custom extractors you often need to access another extractors
597in your implementation.
598
599```rust
600use axum::{
601    async_trait,
602    extract::{Extension, FromRequestParts, TypedHeader},
603    headers::{authorization::Bearer, Authorization},
604    http::{StatusCode, request::Parts},
605    response::{IntoResponse, Response},
606    routing::get,
607    Router,
608};
609
610#[derive(Clone)]
611struct State {
612    // ...
613}
614
615struct AuthenticatedUser {
616    // ...
617}
618
619#[async_trait]
620impl<S> FromRequestParts<S> for AuthenticatedUser
621where
622    S: Send + Sync,
623{
624    type Rejection = Response;
625
626    async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
627        // You can either call them directly...
628        let TypedHeader(Authorization(token)) =
629            TypedHeader::<Authorization<Bearer>>::from_request_parts(parts, state)
630                .await
631                .map_err(|err| err.into_response())?;
632
633        // ... or use `extract` / `extract_with_state` from `RequestExt` / `RequestPartsExt`
634        use axum::RequestPartsExt;
635        let Extension(state) = parts.extract::<Extension<State>>()
636            .await
637            .map_err(|err| err.into_response())?;
638
639        unimplemented!("actually perform the authorization")
640    }
641}
642
643async fn handler(user: AuthenticatedUser) {
644    // ...
645}
646
647let state = State { /* ... */ };
648
649let app = Router::new().route("/", get(handler)).layer(Extension(state));
650# async {
651# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
652# };
653```
654
655# Request body limits
656
657For security reasons, [`Bytes`] will, by default, not accept bodies larger than
6582MB. This also applies to extractors that uses [`Bytes`] internally such as
659`String`, [`Json`], and [`Form`].
660
661For more details, including how to disable this limit, see [`DefaultBodyLimit`].
662
663# Request body extractors
664
665Most of the time your request body type will be [`body::Body`] (a re-export
666of [`hyper::Body`]), which is directly supported by all extractors.
667
668However if you're applying a tower middleware that changes the request body type
669you might have to apply a different body type to some extractors:
670
671```rust
672use std::{
673    task::{Context, Poll},
674    pin::Pin,
675};
676use tower_http::map_request_body::MapRequestBodyLayer;
677use axum::{
678    extract::{self, BodyStream},
679    body::{Body, HttpBody},
680    routing::get,
681    http::{header::HeaderMap, Request},
682    Router,
683};
684
685struct MyBody<B>(B);
686
687impl<B> HttpBody for MyBody<B>
688where
689    B: HttpBody + Unpin,
690{
691    type Data = B::Data;
692    type Error = B::Error;
693
694    fn poll_data(
695        mut self: Pin<&mut Self>,
696        cx: &mut Context<'_>,
697    ) -> Poll<Option<Result<Self::Data, Self::Error>>> {
698        Pin::new(&mut self.0).poll_data(cx)
699    }
700
701    fn poll_trailers(
702        mut self: Pin<&mut Self>,
703        cx: &mut Context<'_>,
704    ) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
705        Pin::new(&mut self.0).poll_trailers(cx)
706    }
707}
708
709let app = Router::new()
710    .route(
711        "/string",
712        // `String` works directly with any body type
713        get(|_: String| async {})
714    )
715    .route(
716        "/body",
717        // `extract::Body` defaults to `axum::body::Body`
718        // but can be customized
719        get(|_: extract::RawBody<MyBody<Body>>| async {})
720    )
721    .route(
722        "/body-stream",
723        // same for `extract::BodyStream`
724        get(|_: extract::BodyStream| async {}),
725    )
726    .route(
727        // and `Request<_>`
728        "/request",
729        get(|_: Request<MyBody<Body>>| async {})
730    )
731    // middleware that changes the request body type
732    .layer(MapRequestBodyLayer::new(MyBody));
733# async {
734# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
735# };
736```
737
738# Running extractors from middleware
739
740Extractors can also be run from middleware:
741
742```rust
743use axum::{
744    middleware::{self, Next},
745    extract::{TypedHeader, FromRequestParts},
746    http::{Request, StatusCode},
747    response::Response,
748    headers::authorization::{Authorization, Bearer},
749    RequestPartsExt, Router,
750};
751
752async fn auth_middleware<B>(
753    request: Request<B>,
754    next: Next<B>,
755) -> Result<Response, StatusCode>
756where
757    B: Send,
758{
759    // running extractors requires a `axum::http::request::Parts`
760    let (mut parts, body) = request.into_parts();
761
762    // `TypedHeader<Authorization<Bearer>>` extracts the auth token
763    let auth: TypedHeader<Authorization<Bearer>> = parts.extract()
764        .await
765        .map_err(|_| StatusCode::UNAUTHORIZED)?;
766
767    if !token_is_valid(auth.token()) {
768        return Err(StatusCode::UNAUTHORIZED);
769    }
770
771    // reconstruct the request
772    let request = Request::from_parts(parts, body);
773
774    Ok(next.run(request).await)
775}
776
777fn token_is_valid(token: &str) -> bool {
778    // ...
779    # false
780}
781
782let app = Router::new().layer(middleware::from_fn(auth_middleware));
783# let _: Router<()> = app;
784```
785
786# Wrapping extractors
787
788If you want write an extractor that generically wraps another extractor (that
789may or may not consume the request body) you should implement both
790[`FromRequest`] and [`FromRequestParts`]:
791
792```rust
793use axum::{
794    Router,
795    routing::get,
796    extract::{FromRequest, FromRequestParts},
797    http::{Request, HeaderMap, request::Parts},
798    async_trait,
799};
800use std::time::{Instant, Duration};
801
802// an extractor that wraps another and measures how long time it takes to run
803struct Timing<E> {
804    extractor: E,
805    duration: Duration,
806}
807
808// we must implement both `FromRequestParts`
809#[async_trait]
810impl<S, T> FromRequestParts<S> for Timing<T>
811where
812    S: Send + Sync,
813    T: FromRequestParts<S>,
814{
815    type Rejection = T::Rejection;
816
817    async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
818        let start = Instant::now();
819        let extractor = T::from_request_parts(parts, state).await?;
820        let duration = start.elapsed();
821        Ok(Timing {
822            extractor,
823            duration,
824        })
825    }
826}
827
828// and `FromRequest`
829#[async_trait]
830impl<S, B, T> FromRequest<S, B> for Timing<T>
831where
832    B: Send + 'static,
833    S: Send + Sync,
834    T: FromRequest<S, B>,
835{
836    type Rejection = T::Rejection;
837
838    async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
839        let start = Instant::now();
840        let extractor = T::from_request(req, state).await?;
841        let duration = start.elapsed();
842        Ok(Timing {
843            extractor,
844            duration,
845        })
846    }
847}
848
849async fn handler(
850    // this uses the `FromRequestParts` impl
851    _: Timing<HeaderMap>,
852    // this uses the `FromRequest` impl
853    _: Timing<String>,
854) {}
855# let _: axum::routing::MethodRouter = axum::routing::get(handler);
856```
857
858# Logging rejections
859
860All built-in extractors will log rejections for easier debugging. To see the
861logs, enable the `tracing` feature for axum and the `axum::rejection=trace`
862tracing target, for example with `RUST_LOG=info,axum::rejection=trace cargo
863run`.
864
865[`body::Body`]: crate::body::Body
866[`Bytes`]: crate::body::Bytes
867[customize-extractor-error]: https://github.com/tokio-rs/axum/blob/main/examples/customize-extractor-error/src/main.rs
868[`HeaderMap`]: https://docs.rs/http/latest/http/header/struct.HeaderMap.html
869[`Request`]: https://docs.rs/http/latest/http/struct.Request.html
870[`RequestParts::body_mut`]: crate::extract::RequestParts::body_mut
871[`JsonRejection::JsonDataError`]: rejection::JsonRejection::JsonDataError
872