1+ use axum:: http:: { HeaderMap , Uri } ;
2+ use serde:: de:: DeserializeOwned ;
13use serde:: { Deserialize , Serialize } ;
24use std:: collections:: HashMap ;
35
4- use axum:: http:: { HeaderMap , Uri } ;
5-
66/// Location must match client side interface
77#[ derive( Serialize , Debug ) ]
88pub struct Location {
@@ -70,12 +70,40 @@ impl Request {
7070 "Failed to read body" ,
7171 ) ) )
7272 }
73+
74+ pub fn form_data < T > ( & self ) -> Result < T , BodyParseError >
75+ where
76+ T : DeserializeOwned ,
77+ {
78+ let content_type = self
79+ . headers
80+ . get ( "content-type" )
81+ . and_then ( |v| v. to_str ( ) . ok ( ) )
82+ . unwrap_or ( "" ) ;
83+
84+ if !content_type. contains ( "application/x-www-form-urlencoded" ) {
85+ return Err ( BodyParseError :: ContentType (
86+ "Invalid content type, expected application/x-www-form-urlencoded" . to_string ( ) ,
87+ ) ) ;
88+ }
89+
90+ let body = self . body . as_ref ( ) . ok_or_else ( || {
91+ BodyParseError :: Io ( std:: io:: Error :: new (
92+ std:: io:: ErrorKind :: InvalidData ,
93+ "Missing request body" ,
94+ ) )
95+ } ) ?;
96+
97+ serde_urlencoded:: from_bytes :: < T > ( body) . map_err ( BodyParseError :: UrlEncoded )
98+ }
7399}
74100
75101#[ derive( Debug ) ]
76102pub enum BodyParseError {
77103 Io ( std:: io:: Error ) ,
78104 Serde ( serde_json:: Error ) ,
105+ UrlEncoded ( serde_urlencoded:: de:: Error ) ,
106+ ContentType ( String ) ,
79107}
80108
81109impl From < serde_json:: Error > for BodyParseError {
@@ -95,6 +123,12 @@ mod tests {
95123 field2 : String ,
96124 }
97125
126+ #[ derive( Debug , Deserialize ) ]
127+ struct FormData {
128+ name : String ,
129+ email : Option < String > ,
130+ }
131+
98132 #[ test]
99133 fn it_correctly_parse_the_body ( ) {
100134 let request = Request :: new (
@@ -123,4 +157,70 @@ mod tests {
123157
124158 assert ! ( body. is_err( ) ) ;
125159 }
160+
161+ #[ test]
162+ fn it_correctly_parses_form_data ( ) {
163+ let mut request = Request :: new (
164+ Uri :: from_static ( "http://localhost:3000" ) ,
165+ HeaderMap :: new ( ) ,
166+ HashMap :: new ( ) ,
167+ None ,
168+ ) ;
169+
170+ request. headers . insert (
171+ "content-type" ,
172+ "application/x-www-form-urlencoded" . parse ( ) . unwrap ( ) ,
173+ ) ;
174+
175+ request. body = Some ( "name=John+Doe&email=john%40example.com" . as_bytes ( ) . to_vec ( ) ) ;
176+
177+ let form_data: Result < FormData , BodyParseError > = request. form_data ( ) ;
178+
179+ assert ! ( form_data. is_ok( ) ) ;
180+ let data = form_data. unwrap ( ) ;
181+ assert_eq ! ( data. name, "John Doe" ) ;
182+ assert_eq ! ( data. email, Some ( "john@example.com" . to_string( ) ) ) ;
183+ }
184+
185+ #[ test]
186+ fn it_rejects_wrong_form_content_type ( ) {
187+ let mut request = Request :: new (
188+ Uri :: from_static ( "http://localhost:3000" ) ,
189+ HeaderMap :: new ( ) ,
190+ HashMap :: new ( ) ,
191+ None ,
192+ ) ;
193+
194+ request
195+ . headers
196+ . insert ( "content-type" , "application/json" . parse ( ) . unwrap ( ) ) ;
197+
198+ request. headers . insert (
199+ "body" ,
200+ "name=John+Doe&email=john%40example.com" . parse ( ) . unwrap ( ) ,
201+ ) ;
202+
203+ let form_data: Result < FormData , BodyParseError > = request. form_data ( ) ;
204+
205+ assert ! ( form_data. is_err( ) ) ;
206+ }
207+
208+ #[ test]
209+ fn it_handles_missing_form_body ( ) {
210+ let mut request = Request :: new (
211+ Uri :: from_static ( "http://localhost:3000" ) ,
212+ HeaderMap :: new ( ) ,
213+ HashMap :: new ( ) ,
214+ None ,
215+ ) ;
216+
217+ request. headers . insert (
218+ "content-type" ,
219+ "application/x-www-form-urlencoded" . parse ( ) . unwrap ( ) ,
220+ ) ;
221+
222+ let form_data: Result < FormData , BodyParseError > = request. form_data ( ) ;
223+
224+ assert ! ( form_data. is_err( ) ) ;
225+ }
126226}
0 commit comments