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