From 2353ee788c55a9dd7dec213ae16c612ef425e6f1 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Tue, 10 May 2022 13:00:57 +0200 Subject: [PATCH] Make docs around extracting `Request` more explicit (#1013) --- axum/src/docs/extract.md | 87 ++++++++++++++++++++++++++++++++-------- 1 file changed, 70 insertions(+), 17 deletions(-) diff --git a/axum/src/docs/extract.md b/axum/src/docs/extract.md index 238273fe..ba5d2784 100644 --- a/axum/src/docs/extract.md +++ b/axum/src/docs/extract.md @@ -136,31 +136,83 @@ async fn get_user_things( # }; ``` -Take care of the order in which you apply extractors as some will mutate the -request. For example extractors that consume the request body can only be -applied once. The same is true for [`Request`], which consumes the entire -request: +Extractors always run in the order of the function parameters that is from +left to right. + +# Be careful when extracting `Request` + +[`Request`] is itself an extractor: ```rust,no_run -use axum::{ - routing::get, - http::Request, - body::Body, - Router, -}; +use axum::{http::Request, body::Body}; async fn handler(request: Request) { // ... } - -let app = Router::new().route("/", get(handler)); -# async { -# axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); -# }; ``` -Extractors always run in the order of the function parameters that is from -left to right. +However be careful when combining it with other extractors since it will consume +all extensions and the request body. Therefore it is recommended to always apply +the request extractor last: + +```rust,no_run +use axum::{http::Request, Extension, body::Body}; + +// this will fail at runtime since `Request` will have consumed all the +// extensions so `Extension` will be missing +async fn broken( + request: Request, + Extension(state): Extension, +) { + // ... +} + +// this will work since we extract `Extension` before `Request` +async fn works( + Extension(state): Extension, + request: Request, +) { + // ... +} + +#[derive(Clone)] +struct State {}; +``` + +# Extracting request bodies + +Since request bodies are asynchronous streams they can only be extracted once: + +```rust,no_run +use axum::{Json, http::Request, body::{Bytes, Body}}; +use serde_json::Value; + +// this will fail at runtime since `Json` and `Bytes` both attempt to extract +// the body +// +// the solution is to only extract the body once so remove either +// `body_json: Json` or `body_bytes: Bytes` +async fn broken( + body_json: Json, + body_bytes: Bytes, +) { + // ... +} + +// this doesn't work either for the same reason: `Bytes` and `Request` +// both extract the body +async fn also_broken( + body_json: Json, + request: Request, +) { + // ... +} +``` + +Also keep this in mind if you extract or otherwise consume the body in +middleware. You either need to not extract the body in handlers or make sure +your middleware reinserts the body using [`RequestParts::body_mut`] so it's +available to handlers. # Optional extractors @@ -514,3 +566,4 @@ let app = Router::new() [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 +[`RequestParts::body_mut`]: crate::extract::RequestParts::body_mut