axum_core/extract/
request_parts.rs

1use super::{rejection::*, FromRequest, FromRequestParts, Request};
2use crate::{body::Body, RequestExt};
3use bytes::{BufMut, Bytes, BytesMut};
4use http::{request::Parts, Extensions, HeaderMap, Method, Uri, Version};
5use http_body_util::BodyExt;
6use std::convert::Infallible;
7
8impl<S> FromRequest<S> for Request
9where
10    S: Send + Sync,
11{
12    type Rejection = Infallible;
13
14    async fn from_request(req: Request, _: &S) -> Result<Self, Self::Rejection> {
15        Ok(req)
16    }
17}
18
19impl<S> FromRequestParts<S> for Method
20where
21    S: Send + Sync,
22{
23    type Rejection = Infallible;
24
25    async fn from_request_parts(parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
26        Ok(parts.method.clone())
27    }
28}
29
30impl<S> FromRequestParts<S> for Uri
31where
32    S: Send + Sync,
33{
34    type Rejection = Infallible;
35
36    async fn from_request_parts(parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
37        Ok(parts.uri.clone())
38    }
39}
40
41impl<S> FromRequestParts<S> for Version
42where
43    S: Send + Sync,
44{
45    type Rejection = Infallible;
46
47    async fn from_request_parts(parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
48        Ok(parts.version)
49    }
50}
51
52/// Clone the headers from the request.
53///
54/// Prefer using [`TypedHeader`] to extract only the headers you need.
55///
56/// [`TypedHeader`]: https://docs.rs/axum-extra/0.10/axum_extra/struct.TypedHeader.html
57impl<S> FromRequestParts<S> for HeaderMap
58where
59    S: Send + Sync,
60{
61    type Rejection = Infallible;
62
63    async fn from_request_parts(parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
64        Ok(parts.headers.clone())
65    }
66}
67
68#[diagnostic::do_not_recommend] // pretty niche impl
69impl<S> FromRequest<S> for BytesMut
70where
71    S: Send + Sync,
72{
73    type Rejection = BytesRejection;
74
75    async fn from_request(req: Request, _: &S) -> Result<Self, Self::Rejection> {
76        let mut body = req.into_limited_body();
77        #[allow(clippy::use_self)]
78        let mut bytes = BytesMut::new();
79        body_to_bytes_mut(&mut body, &mut bytes).await?;
80        Ok(bytes)
81    }
82}
83
84async fn body_to_bytes_mut(body: &mut Body, bytes: &mut BytesMut) -> Result<(), BytesRejection> {
85    while let Some(frame) = body
86        .frame()
87        .await
88        .transpose()
89        .map_err(FailedToBufferBody::from_err)?
90    {
91        let Ok(data) = frame.into_data() else {
92            return Ok(());
93        };
94        bytes.put(data);
95    }
96
97    Ok(())
98}
99
100impl<S> FromRequest<S> for Bytes
101where
102    S: Send + Sync,
103{
104    type Rejection = BytesRejection;
105
106    async fn from_request(req: Request, _: &S) -> Result<Self, Self::Rejection> {
107        let bytes = req
108            .into_limited_body()
109            .collect()
110            .await
111            .map_err(FailedToBufferBody::from_err)?
112            .to_bytes();
113
114        Ok(bytes)
115    }
116}
117
118impl<S> FromRequest<S> for String
119where
120    S: Send + Sync,
121{
122    type Rejection = StringRejection;
123
124    async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
125        let bytes = Bytes::from_request(req, state)
126            .await
127            .map_err(|err| match err {
128                BytesRejection::FailedToBufferBody(inner) => {
129                    StringRejection::FailedToBufferBody(inner)
130                }
131            })?;
132
133        #[allow(clippy::use_self)]
134        let string = String::from_utf8(bytes.into()).map_err(InvalidUtf8::from_err)?;
135
136        Ok(string)
137    }
138}
139
140#[diagnostic::do_not_recommend] // pretty niche impl
141impl<S> FromRequestParts<S> for Parts
142where
143    S: Send + Sync,
144{
145    type Rejection = Infallible;
146
147    async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
148        Ok(parts.clone())
149    }
150}
151
152#[diagnostic::do_not_recommend] // pretty niche impl
153impl<S> FromRequestParts<S> for Extensions
154where
155    S: Send + Sync,
156{
157    type Rejection = Infallible;
158
159    async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
160        Ok(parts.extensions.clone())
161    }
162}
163
164impl<S> FromRequest<S> for Body
165where
166    S: Send + Sync,
167{
168    type Rejection = Infallible;
169
170    async fn from_request(req: Request, _: &S) -> Result<Self, Self::Rejection> {
171        Ok(req.into_body())
172    }
173}
174
175#[cfg(test)]
176mod tests {
177    use axum::{extract::Extension, routing::get, test_helpers::*, Router};
178    use http::{Method, StatusCode};
179
180    #[crate::test]
181    async fn extract_request_parts() {
182        #[derive(Clone)]
183        struct Ext;
184
185        async fn handler(parts: http::request::Parts) {
186            assert_eq!(parts.method, Method::GET);
187            assert_eq!(parts.uri, "/");
188            assert_eq!(parts.version, http::Version::HTTP_11);
189            assert_eq!(parts.headers["x-foo"], "123");
190            parts.extensions.get::<Ext>().unwrap();
191        }
192
193        let client = TestClient::new(Router::new().route("/", get(handler)).layer(Extension(Ext)));
194
195        let res = client.get("/").header("x-foo", "123").await;
196        assert_eq!(res.status(), StatusCode::OK);
197    }
198}