Skip to content

Commit 6564bb0

Browse files
committed
refactor(core): 优化请求解析方法的语义分离
- 将 application/x-www-form-urlencoded 处理从 json_parse 移到 form_parse - json_parse 现在仅处理 application/json 数据,移除不必要的 Serialize 约束 - form_parse 支持 multipart/form-data 和 application/x-www-form-urlencoded - 实现统一缓存策略:WWW_FORM_URLENCODED 数据缓存到 json_data 字段 - 为 multipart/form-data 复用 form_data 缓存机制 - 添加完整的测试用例验证语义分离和缓存机制 - 更新 examples/form 示例使用正确的 form_parse 方法 BREAKING CHANGE: json_parse 不再支持 application/x-www-form-urlencoded 迁移指南:使用 form_parse() 替代 json_parse() 处理表单数据
1 parent 35d711d commit 6564bb0

File tree

2 files changed

+214
-48
lines changed

2 files changed

+214
-48
lines changed

examples/form/src/main.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ struct Input {
1515
}
1616

1717
async fn accept_form(mut req: Request) -> Result<Input> {
18-
req.json_parse().await
18+
// 使用 form_parse 来处理表单数据(支持 application/x-www-form-urlencoded 和 multipart/form-data)
19+
req.form_parse().await
1920
}
2021

2122
async fn show_form(_req: Request) -> Result<Response> {

silent/src/core/request.rs

Lines changed: 212 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -306,16 +306,59 @@ impl Request {
306306
.await
307307
}
308308

309-
/// 获取请求form_data
310-
#[cfg(feature = "multipart")]
311-
#[inline]
309+
/// 解析表单数据(支持 multipart/form-data 和 application/x-www-form-urlencoded)
312310
pub async fn form_parse<T>(&mut self) -> Result<T>
313311
where
314-
for<'de> T: Deserialize<'de>,
312+
for<'de> T: Deserialize<'de> + Serialize,
315313
{
316-
let form_data = self.form_data().await?;
317-
let value = serde_json::to_value(form_data.fields.clone())?;
318-
serde_json::from_value(value).map_err(Into::into)
314+
let content_type = self
315+
.content_type()
316+
.ok_or(SilentError::ContentTypeMissingError)?;
317+
318+
match content_type.subtype() {
319+
#[cfg(feature = "multipart")]
320+
mime::FORM_DATA => {
321+
// 复用 form_data 的缓存机制
322+
let form_data = self.form_data().await?;
323+
let value =
324+
serde_json::to_value(form_data.fields.clone()).map_err(SilentError::from)?;
325+
serde_json::from_value(value).map_err(Into::into)
326+
}
327+
mime::WWW_FORM_URLENCODED => {
328+
// 检查是否已缓存到 json_data
329+
if let Some(cached_value) = self.json_data.get() {
330+
return serde_json::from_value(cached_value.clone()).map_err(Into::into);
331+
}
332+
333+
// 解析 form-urlencoded 数据并缓存到 json_data
334+
let body = self.take_body();
335+
let bytes = match body {
336+
ReqBody::Incoming(body) => body
337+
.collect()
338+
.await
339+
.or(Err(SilentError::BodyEmpty))?
340+
.to_bytes(),
341+
ReqBody::Once(bytes) => bytes,
342+
ReqBody::Empty => return Err(SilentError::BodyEmpty),
343+
};
344+
345+
if bytes.is_empty() {
346+
return Err(SilentError::BodyEmpty);
347+
}
348+
349+
// 先解析为目标类型
350+
let parsed_data: T =
351+
serde_html_form::from_bytes(&bytes).map_err(SilentError::from)?;
352+
353+
// 转换为 Value 并缓存(需要重新导入 Serialize)
354+
let value = serde_json::to_value(&parsed_data).map_err(SilentError::from)?;
355+
let _ = self.json_data.set(value.clone());
356+
357+
// 直接返回已解析的数据
358+
Ok(parsed_data)
359+
}
360+
_ => Err(SilentError::ContentTypeError),
361+
}
319362
}
320363

321364
/// 转换body参数
@@ -341,55 +384,44 @@ impl Request {
341384
.and_then(|ps| ps.files.get_vec(key))
342385
}
343386

344-
/// 转换body参数按Json匹配
387+
/// 解析 JSON 数据(仅支持 application/json)
345388
pub async fn json_parse<T>(&mut self) -> Result<T>
346389
where
347390
for<'de> T: Deserialize<'de>,
348-
T: Serialize,
349391
{
350-
let body = self.take_body();
392+
// 检查是否已缓存
393+
if let Some(cached_value) = self.json_data.get() {
394+
return serde_json::from_value(cached_value.clone()).map_err(Into::into);
395+
}
396+
351397
let content_type = self
352398
.content_type()
353399
.ok_or(SilentError::ContentTypeMissingError)?;
354-
if content_type.subtype() != mime::JSON
355-
&& content_type.subtype() != mime::WWW_FORM_URLENCODED
356-
{
400+
401+
if content_type.subtype() != mime::JSON {
357402
return Err(SilentError::ContentTypeError);
358403
}
359-
let value = self
360-
.json_data
361-
.get_or_try_init(|| async {
362-
let value = match body {
363-
ReqBody::Incoming(body) => {
364-
let bytes = body
365-
.collect()
366-
.await
367-
.or(Err(SilentError::JsonEmpty))?
368-
.to_bytes();
369-
if bytes.is_empty() {
370-
return Err(SilentError::JsonEmpty);
371-
}
372-
match content_type.subtype() {
373-
mime::WWW_FORM_URLENCODED => {
374-
serde_html_form::from_bytes::<T>(&bytes).map_err(SilentError::from)
375-
}
376-
mime::JSON => serde_json::from_slice::<T>(&bytes).map_err(|e| e.into()),
377-
_ => Err(SilentError::JsonEmpty),
378-
}
379-
}
380-
ReqBody::Once(bytes) => match content_type.subtype() {
381-
mime::WWW_FORM_URLENCODED => {
382-
serde_html_form::from_bytes(&bytes).map_err(SilentError::from)
383-
}
384-
mime::JSON => serde_json::from_slice(&bytes).map_err(|e| e.into()),
385-
_ => Err(SilentError::JsonEmpty),
386-
},
387-
ReqBody::Empty => Err(SilentError::BodyEmpty),
388-
}?;
389-
serde_json::to_value(&value).map_err(|e| e.into())
390-
})
391-
.await?
392-
.clone();
404+
405+
let body = self.take_body();
406+
let bytes = match body {
407+
ReqBody::Incoming(body) => body
408+
.collect()
409+
.await
410+
.or(Err(SilentError::JsonEmpty))?
411+
.to_bytes(),
412+
ReqBody::Once(bytes) => bytes,
413+
ReqBody::Empty => return Err(SilentError::JsonEmpty),
414+
};
415+
416+
if bytes.is_empty() {
417+
return Err(SilentError::JsonEmpty);
418+
}
419+
420+
let value: Value = serde_json::from_slice(&bytes).map_err(SilentError::from)?;
421+
422+
// 缓存结果
423+
let _ = self.json_data.set(value.clone());
424+
393425
serde_json::from_value(value).map_err(Into::into)
394426
}
395427

@@ -452,4 +484,137 @@ mod tests {
452484
*req.uri_mut() = Uri::from_static("http://localhost:8080/test?a=1&b=2&c=3&c=4");
453485
let _ = req.params_parse::<TestStruct>().unwrap();
454486
}
487+
488+
/// 测试 json_parse 和 form_parse 的语义分离
489+
#[tokio::test]
490+
async fn test_methods_semantic_separation() {
491+
// 测试数据结构,现在需要 Serialize 和 Deserialize
492+
#[derive(Deserialize, Serialize, Debug, PartialEq)]
493+
struct TestData {
494+
name: String,
495+
age: u32,
496+
}
497+
498+
let test_data = TestData {
499+
name: "Alice".to_string(),
500+
age: 25,
501+
};
502+
503+
// 1. json_parse 正确处理 JSON 数据
504+
let json_body = r#"{"name":"Alice","age":25}"#.as_bytes().to_vec();
505+
let mut req = create_request_with_body("application/json", json_body);
506+
507+
let parsed_data = req
508+
.json_parse::<TestData>()
509+
.await
510+
.expect("json_parse should successfully parse JSON data");
511+
assert_eq!(parsed_data.name, test_data.name);
512+
assert_eq!(parsed_data.age, test_data.age);
513+
514+
// 2. form_parse 正确处理 form-urlencoded 数据
515+
let form_body = "name=Alice&age=25".as_bytes().to_vec();
516+
let mut req = create_request_with_body("application/x-www-form-urlencoded", form_body);
517+
518+
let parsed_data = req
519+
.form_parse::<TestData>()
520+
.await
521+
.expect("form_parse should successfully parse form-urlencoded data");
522+
assert_eq!(parsed_data.name, test_data.name);
523+
assert_eq!(parsed_data.age, test_data.age);
524+
525+
// 3. json_parse 拒绝 form-urlencoded 数据
526+
let form_body = "name=Alice&age=25".as_bytes().to_vec();
527+
let mut req = create_request_with_body("application/x-www-form-urlencoded", form_body);
528+
529+
let result = req.json_parse::<TestData>().await;
530+
assert!(
531+
result.is_err(),
532+
"json_parse should reject form-urlencoded data"
533+
);
534+
535+
// 4. form_parse 拒绝 JSON 数据
536+
let json_body = r#"{"name":"Alice","age":25}"#.as_bytes().to_vec();
537+
let mut req = create_request_with_body("application/json", json_body);
538+
539+
let result = req.form_parse::<TestData>().await;
540+
assert!(result.is_err(), "form_parse should reject JSON data");
541+
}
542+
543+
/// 测试 WWW_FORM_URLENCODED 数据缓存到 json_data 字段
544+
#[tokio::test]
545+
async fn test_form_urlencoded_caches_to_json_data() {
546+
#[derive(Deserialize, Serialize, Debug, PartialEq)]
547+
struct TestData {
548+
name: String,
549+
age: u32,
550+
}
551+
552+
// 创建一个 form-urlencoded 请求
553+
let form_body = "name=Alice&age=25".as_bytes().to_vec();
554+
let mut req = create_request_with_body("application/x-www-form-urlencoded", form_body);
555+
556+
// 第一次调用 form_parse,应该解析数据并缓存到 json_data
557+
let first_result = req
558+
.form_parse::<TestData>()
559+
.await
560+
.expect("First form_parse call should succeed");
561+
562+
// 验证 json_data 字段已被缓存
563+
assert!(
564+
req.json_data.get().is_some(),
565+
"json_data should be cached after form_parse"
566+
);
567+
568+
// 第二次调用应该从缓存中获取(不会再次解析 body)
569+
let second_result = req
570+
.form_parse::<TestData>()
571+
.await
572+
.expect("Second form_parse call should use cached data");
573+
574+
// 两次结果应该相同
575+
assert_eq!(first_result.name, second_result.name);
576+
assert_eq!(first_result.age, second_result.age);
577+
assert_eq!(first_result.name, "Alice");
578+
assert_eq!(first_result.age, 25);
579+
}
580+
581+
/// 测试共享缓存机制(验证 form_parse 复用 form_data 缓存)
582+
#[cfg(feature = "multipart")]
583+
#[tokio::test]
584+
async fn test_shared_cache_mechanism() {
585+
// 简单验证:当 Content-Type 是 multipart/form-data 时,
586+
// form_parse 会调用 form_data() 方法,从而复用其缓存
587+
let mut req = Request::empty();
588+
req.headers_mut().insert(
589+
"content-type",
590+
HeaderValue::from_str("multipart/form-data; boundary=----formdata").unwrap(),
591+
);
592+
593+
// 设置一个空的 body 来避免实际的 multipart 解析
594+
req.body = ReqBody::Empty;
595+
596+
// 尝试调用 form_parse,它应该尝试使用 form_data() 方法
597+
// 这个测试主要验证代码路径,而不是具体的数据解析
598+
#[derive(Deserialize, Serialize, Debug)]
599+
struct TestData {
600+
name: String,
601+
}
602+
603+
let result = req.form_parse::<TestData>().await;
604+
// 预期会失败,因为我们没有提供真实的 multipart 数据
605+
// 但重要的是代码走了正确的路径(调用 form_data())
606+
assert!(
607+
result.is_err(),
608+
"Should fail due to empty body, but went through correct code path"
609+
);
610+
}
611+
612+
/// 辅助函数:创建带有指定内容类型和内容的请求
613+
fn create_request_with_body(content_type: &str, body: Vec<u8>) -> Request {
614+
let mut req = Request::empty();
615+
req.headers_mut()
616+
.insert("content-type", HeaderValue::from_str(content_type).unwrap());
617+
req.body = ReqBody::Once(body.into());
618+
req
619+
}
455620
}

0 commit comments

Comments
 (0)