1 use async_trait::async_trait; 2 use axum_core::extract::FromRequest; 3 use bytes::{Bytes, BytesMut}; 4 use http::{Method, Request}; 5 6 use super::{ 7 has_content_type, 8 rejection::{InvalidFormContentType, RawFormRejection}, 9 }; 10 11 use crate::{body::HttpBody, BoxError}; 12 13 /// Extractor that extracts raw form requests. 14 /// 15 /// For `GET` requests it will extract the raw query. For other methods it extracts the raw 16 /// `application/x-www-form-urlencoded` encoded request body. 17 /// 18 /// # Example 19 /// 20 /// ```rust,no_run 21 /// use axum::{ 22 /// extract::RawForm, 23 /// routing::get, 24 /// Router 25 /// }; 26 /// 27 /// async fn handler(RawForm(form): RawForm) {} 28 /// 29 /// let app = Router::new().route("/", get(handler)); 30 /// # async { 31 /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); 32 /// # }; 33 /// ``` 34 #[derive(Debug)] 35 pub struct RawForm(pub Bytes); 36 37 #[async_trait] 38 impl<S, B> FromRequest<S, B> for RawForm 39 where 40 B: HttpBody + Send + 'static, 41 B::Data: Send, 42 B::Error: Into<BoxError>, 43 S: Send + Sync, 44 { 45 type Rejection = RawFormRejection; 46 from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection>47 async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> { 48 if req.method() == Method::GET { 49 let mut bytes = BytesMut::new(); 50 51 if let Some(query) = req.uri().query() { 52 bytes.extend(query.as_bytes()); 53 } 54 55 Ok(Self(bytes.freeze())) 56 } else { 57 if !has_content_type(req.headers(), &mime::APPLICATION_WWW_FORM_URLENCODED) { 58 return Err(InvalidFormContentType.into()); 59 } 60 61 Ok(Self(Bytes::from_request(req, state).await?)) 62 } 63 } 64 } 65 66 #[cfg(test)] 67 mod tests { 68 use http::{header::CONTENT_TYPE, Request}; 69 70 use super::{InvalidFormContentType, RawForm, RawFormRejection}; 71 72 use crate::{ 73 body::{Bytes, Empty, Full}, 74 extract::FromRequest, 75 }; 76 check_query(uri: &str, value: &[u8])77 async fn check_query(uri: &str, value: &[u8]) { 78 let req = Request::builder() 79 .uri(uri) 80 .body(Empty::<Bytes>::new()) 81 .unwrap(); 82 83 assert_eq!(RawForm::from_request(req, &()).await.unwrap().0, value); 84 } 85 check_body(body: &'static [u8])86 async fn check_body(body: &'static [u8]) { 87 let req = Request::post("http://example.com/test") 88 .header(CONTENT_TYPE, mime::APPLICATION_WWW_FORM_URLENCODED.as_ref()) 89 .body(Full::new(Bytes::from(body))) 90 .unwrap(); 91 92 assert_eq!(RawForm::from_request(req, &()).await.unwrap().0, body); 93 } 94 95 #[crate::test] test_from_query()96 async fn test_from_query() { 97 check_query("http://example.com/test", b"").await; 98 99 check_query("http://example.com/test?page=0&size=10", b"page=0&size=10").await; 100 } 101 102 #[crate::test] test_from_body()103 async fn test_from_body() { 104 check_body(b"").await; 105 106 check_body(b"username=user&password=secure%20password").await; 107 } 108 109 #[crate::test] test_incorrect_content_type()110 async fn test_incorrect_content_type() { 111 let req = Request::post("http://example.com/test") 112 .body(Full::<Bytes>::from(Bytes::from("page=0&size=10"))) 113 .unwrap(); 114 115 assert!(matches!( 116 RawForm::from_request(req, &()).await.unwrap_err(), 117 RawFormRejection::InvalidFormContentType(InvalidFormContentType) 118 )) 119 } 120 } 121