1
0
Fork 0
mirror of https://github.com/tokio-rs/axum.git synced 2025-04-26 13:56:22 +02:00

Implement FromRequest for http::request::Parts ()

Its a convenient way to extract everything from a request except the
body. Also makes sense for axum to provide this since other crates
can't.
This commit is contained in:
David Pedersen 2021-11-09 21:37:24 +01:00 committed by GitHub
parent 62be8aaa18
commit fdd889525d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 92 additions and 1 deletions

View file

@ -7,9 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
# Unreleased
- Implement `FromRequest` for [`http::request::Parts`] so it can be used an
extractor ([#489])
- Implement `IntoResponse` for `http::response::Parts` ([#490])
[#489]: https://github.com/tokio-rs/axum/pull/489
[#490]: https://github.com/tokio-rs/axum/pull/490
[`http::request::Parts`]: https://docs.rs/http/latest/http/request/struct.Parts.html
# 0.3.2 (08. November, 2021)

View file

@ -272,6 +272,16 @@ composite_rejection! {
}
}
composite_rejection! {
/// Rejection used for [`http::request::Parts`].
///
/// Contains one variant for each way the [`http::request::Parts`] extractor can fail.
pub enum RequestPartsAlreadyExtracted {
HeadersAlreadyExtracted,
ExtensionsAlreadyExtracted,
}
}
define_rejection! {
#[status = INTERNAL_SERVER_ERROR]
#[body = "No matched path found"]

View file

@ -323,10 +323,49 @@ where
}
}
#[async_trait]
impl<B> FromRequest<B> for http::request::Parts
where
B: Send,
{
type Rejection = RequestPartsAlreadyExtracted;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
let method = unwrap_infallible(Method::from_request(req).await);
let uri = unwrap_infallible(Uri::from_request(req).await);
let version = unwrap_infallible(Version::from_request(req).await);
let headers = HeaderMap::from_request(req).await?;
let extensions = Extensions::from_request(req).await?;
let mut temp_request = Request::new(());
*temp_request.method_mut() = method;
*temp_request.uri_mut() = uri;
*temp_request.version_mut() = version;
*temp_request.headers_mut() = headers;
*temp_request.extensions_mut() = extensions;
let (parts, _) = temp_request.into_parts();
Ok(parts)
}
}
fn unwrap_infallible<T>(result: Result<T, Infallible>) -> T {
match result {
Ok(value) => value,
Err(err) => match err {},
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{body::Body, routing::post, test_helpers::*, Router};
use crate::{
body::Body,
routing::{get, post},
test_helpers::*,
AddExtensionLayer, Router,
};
use http::StatusCode;
#[tokio::test]
@ -344,4 +383,42 @@ mod tests {
"Cannot have two request body extractors for a single handler"
);
}
#[tokio::test]
async fn extract_request_parts() {
#[derive(Clone)]
struct Ext;
async fn handler(parts: http::request::Parts) {
assert_eq!(parts.method, Method::GET);
assert_eq!(parts.uri, "/");
assert_eq!(parts.version, http::Version::HTTP_11);
assert_eq!(parts.headers["x-foo"], "123");
parts.extensions.get::<Ext>().unwrap();
}
let client = TestClient::new(
Router::new()
.route("/", get(handler))
.layer(AddExtensionLayer::new(Ext)),
);
let res = client.get("/").header("x-foo", "123").send().await;
assert_eq!(res.status(), StatusCode::OK);
}
#[tokio::test]
async fn extract_request_parts_doesnt_consume_the_body() {
#[derive(Clone)]
struct Ext;
async fn handler(_parts: http::request::Parts, body: String) {
assert_eq!(body, "foo");
}
let client = TestClient::new(Router::new().route("/", get(handler)));
let res = client.get("/").body("foo").send().await;
assert_eq!(res.status(), StatusCode::OK);
}
}