1use async_trait::async_trait;
2use axum_core::extract::{FromRequest, Request};
3use bytes::Bytes;
4use http::Method;
5
6use super::{
7 has_content_type,
8 rejection::{InvalidFormContentType, RawFormRejection},
9};
10
11#[derive(Debug)]
31pub struct RawForm(pub Bytes);
32
33#[async_trait]
34impl<S> FromRequest<S> for RawForm
35where
36 S: Send + Sync,
37{
38 type Rejection = RawFormRejection;
39
40 async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
41 if req.method() == Method::GET {
42 if let Some(query) = req.uri().query() {
43 return Ok(Self(Bytes::copy_from_slice(query.as_bytes())));
44 }
45
46 Ok(Self(Bytes::new()))
47 } else {
48 if !has_content_type(req.headers(), &mime::APPLICATION_WWW_FORM_URLENCODED) {
49 return Err(InvalidFormContentType.into());
50 }
51
52 Ok(Self(Bytes::from_request(req, state).await?))
53 }
54 }
55}
56
57#[cfg(test)]
58mod tests {
59 use axum_core::body::Body;
60 use http::{header::CONTENT_TYPE, Request};
61
62 use super::{InvalidFormContentType, RawForm, RawFormRejection};
63
64 use crate::extract::FromRequest;
65
66 async fn check_query(uri: &str, value: &[u8]) {
67 let req = Request::builder().uri(uri).body(Body::empty()).unwrap();
68
69 assert_eq!(RawForm::from_request(req, &()).await.unwrap().0, value);
70 }
71
72 async fn check_body(body: &'static [u8]) {
73 let req = Request::post("http://example.com/test")
74 .header(CONTENT_TYPE, mime::APPLICATION_WWW_FORM_URLENCODED.as_ref())
75 .body(Body::from(body))
76 .unwrap();
77
78 assert_eq!(RawForm::from_request(req, &()).await.unwrap().0, body);
79 }
80
81 #[crate::test]
82 async fn test_from_query() {
83 check_query("http://example.com/test", b"").await;
84
85 check_query("http://example.com/test?page=0&size=10", b"page=0&size=10").await;
86 }
87
88 #[crate::test]
89 async fn test_from_body() {
90 check_body(b"").await;
91
92 check_body(b"username=user&password=secure%20password").await;
93 }
94
95 #[crate::test]
96 async fn test_incorrect_content_type() {
97 let req = Request::post("http://example.com/test")
98 .body(Body::from("page=0&size=10"))
99 .unwrap();
100
101 assert!(matches!(
102 RawForm::from_request(req, &()).await.unwrap_err(),
103 RawFormRejection::InvalidFormContentType(InvalidFormContentType)
104 ))
105 }
106}