feat: mock配置迁移至JSON格式并修复body匹配
- 将mock配置从YAML格式迁移到JSON格式 - 修复JSON字符串格式body匹配失败问题 - 添加MCP功能模块 - 更新mock-spec.md规范文档 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -88,45 +88,49 @@ impl MockRouter {
|
||||
}
|
||||
|
||||
// D. Body 匹配(根据 Payload 类型智能比较)
|
||||
if let Some(ref yaml_body) = rule.request.body {
|
||||
return self.match_body(yaml_body, payload);
|
||||
if let Some(ref rule_body) = rule.request.body {
|
||||
return self.match_body(rule_body, payload);
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Body 匹配逻辑
|
||||
fn match_body(&self, yaml_body: &serde_json::Value, payload: &Payload) -> bool {
|
||||
// 将 YAML body 规范化:如果是字符串,尝试解析为 JSON
|
||||
let normalized_body = normalize_yaml_body(yaml_body);
|
||||
|
||||
fn match_body(&self, rule_body: &serde_json::Value, payload: &Payload) -> bool {
|
||||
match payload {
|
||||
Payload::Json(actual) => {
|
||||
// JSON 对象比较
|
||||
&normalized_body == actual
|
||||
// 如果 rule_body 是字符串,尝试解析为 JSON 后比较
|
||||
if let Some(rule_str) = rule_body.as_str() {
|
||||
// 尝试将字符串解析为 JSON
|
||||
if let Ok(parsed_rule) = serde_json::from_str::<serde_json::Value>(rule_str) {
|
||||
return &parsed_rule == actual;
|
||||
}
|
||||
}
|
||||
// 直接比较
|
||||
rule_body == actual
|
||||
}
|
||||
Payload::Xml(actual) => {
|
||||
// XML 字符串比较(规范化后比较)
|
||||
normalized_body.as_str()
|
||||
rule_body.as_str()
|
||||
.map(|expected| normalize_xml(expected) == normalize_xml(actual))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
Payload::Form(actual) => {
|
||||
// Form 键值对比较(子集匹配)
|
||||
compare_form_with_yaml(&normalized_body, actual)
|
||||
compare_form_with_json(rule_body, actual)
|
||||
}
|
||||
Payload::Multipart(actual_data) => {
|
||||
// Multipart 匹配:支持键值对或字段名列表
|
||||
compare_multipart_with_yaml(&normalized_body, actual_data)
|
||||
compare_multipart_with_json(rule_body, actual_data)
|
||||
}
|
||||
Payload::Text(actual) => {
|
||||
// 字符串比较(去掉首尾空白)
|
||||
normalized_body.as_str()
|
||||
rule_body.as_str()
|
||||
.map(|expected| expected.trim() == actual.trim())
|
||||
.unwrap_or_else(|| normalized_body.to_string().trim() == actual.trim())
|
||||
.unwrap_or_else(|| rule_body.to_string().trim() == actual.trim())
|
||||
}
|
||||
Payload::None => {
|
||||
false // YAML 配置了 body,但请求没有 body
|
||||
false // 配置了 body,但请求没有 body
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,18 +145,6 @@ impl MockRouter {
|
||||
}
|
||||
}
|
||||
|
||||
/// 规范化 YAML body:如果是字符串,尝试解析为 JSON
|
||||
fn normalize_yaml_body(yaml_body: &serde_json::Value) -> serde_json::Value {
|
||||
if let Some(s) = yaml_body.as_str() {
|
||||
// 先 trim 去掉前导/尾随空格,再尝试解析为 JSON
|
||||
let trimmed = s.trim();
|
||||
if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(trimmed) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
yaml_body.clone()
|
||||
}
|
||||
|
||||
/// 规范化 XML 字符串:去掉声明、多余空白、格式化为紧凑形式
|
||||
fn normalize_xml(xml: &str) -> String {
|
||||
let mut result = xml.to_string();
|
||||
@@ -182,15 +174,15 @@ fn normalize_xml(xml: &str) -> String {
|
||||
result
|
||||
}
|
||||
|
||||
/// Form 比较:YAML 中的键值对必须是请求的子集
|
||||
fn compare_form_with_yaml(yaml_body: &serde_json::Value, actual: &HashMap<String, String>) -> bool {
|
||||
let yaml_map = match yaml_body.as_object() {
|
||||
/// Form 比较:JSON 中的键值对必须是请求的子集
|
||||
fn compare_form_with_json(rule_body: &serde_json::Value, actual: &HashMap<String, String>) -> bool {
|
||||
let rule_map = match rule_body.as_object() {
|
||||
Some(obj) => obj,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
for (key, yaml_val) in yaml_map {
|
||||
let expected = yaml_val.as_str().map(|s| s.to_string()).unwrap_or_else(|| yaml_val.to_string());
|
||||
for (key, rule_val) in rule_map {
|
||||
let expected = rule_val.as_str().map(|s| s.to_string()).unwrap_or_else(|| rule_val.to_string());
|
||||
if actual.get(key) != Some(&expected) {
|
||||
return false;
|
||||
}
|
||||
@@ -199,10 +191,10 @@ fn compare_form_with_yaml(yaml_body: &serde_json::Value, actual: &HashMap<String
|
||||
}
|
||||
|
||||
/// Multipart 比较:对象和数组形式都只匹配字段名是否存在
|
||||
fn compare_multipart_with_yaml(yaml_body: &serde_json::Value, actual: &HashMap<String, String>) -> bool {
|
||||
fn compare_multipart_with_json(rule_body: &serde_json::Value, actual: &HashMap<String, String>) -> bool {
|
||||
// 方式 1:对象形式 - 只匹配键名是否存在(忽略值)
|
||||
if let Some(yaml_map) = yaml_body.as_object() {
|
||||
for key in yaml_map.keys() {
|
||||
if let Some(rule_map) = rule_body.as_object() {
|
||||
for key in rule_map.keys() {
|
||||
if !actual.contains_key(key) {
|
||||
return false;
|
||||
}
|
||||
@@ -211,9 +203,9 @@ fn compare_multipart_with_yaml(yaml_body: &serde_json::Value, actual: &HashMap<S
|
||||
}
|
||||
|
||||
// 方式 2:数组形式 - 只匹配字段名是否存在
|
||||
if let Some(yaml_array) = yaml_body.as_array() {
|
||||
for yaml_field in yaml_array {
|
||||
let field_name = yaml_field.as_str().map(|s| s.to_string()).unwrap_or_else(|| yaml_field.to_string());
|
||||
if let Some(rule_array) = rule_body.as_array() {
|
||||
for rule_field in rule_array {
|
||||
let field_name = rule_field.as_str().map(|s| s.to_string()).unwrap_or_else(|| rule_field.to_string());
|
||||
if !actual.contains_key(&field_name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user