This is an automated email from the ASF dual-hosted git repository.

Aias00 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git


The following commit(s) were added to refs/heads/master by this push:
     new d0a0ddd2ab feature (plugin-mock) : support form request body 
expressions. (#7394)
d0a0ddd2ab is described below

commit d0a0ddd2abc713f92be81f6a68320c4bad6fc682
Author: Jerry聊AI <[email protected]>
AuthorDate: Thu Oct 1 20:45:26 2026 +0800

    feature (plugin-mock) : support form request body expressions. (#7394)
---
 .../apache/shenyu/plugin/mock/api/MockRequest.java | 28 +++++++-
 .../shenyu/plugin/mock/api/MockRequestTest.java    | 79 ++++++++++++++++++++++
 .../mock/generator/ExpressionGeneratorTest.java    | 10 +++
 3 files changed, 114 insertions(+), 3 deletions(-)

diff --git 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/api/MockRequest.java
 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/api/MockRequest.java
index 0543e2f661..c38ffec367 100644
--- 
a/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/api/MockRequest.java
+++ 
b/shenyu-plugin/shenyu-plugin-mock/src/main/java/org/apache/shenyu/plugin/mock/api/MockRequest.java
@@ -19,6 +19,9 @@ package org.apache.shenyu.plugin.mock.api;
 
 import org.apache.shenyu.common.utils.JsonUtils;
 
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Objects;
 
@@ -36,6 +39,8 @@ public final class MockRequest {
 
     private Object json;
 
+    private Map<String, String> forms;
+
     private MockRequest() {
     }
 
@@ -88,13 +93,30 @@ public final class MockRequest {
     }
 
     /**
-     * Gets form-body,not support now.
+     * Gets form-body.
      *
      * @return form-body
      */
     public Map<String, String> getForms() {
-        //todo
-        throw new UnsupportedOperationException();
+        if (Objects.isNull(forms)) {
+            Map<String, String> parsedForms = new LinkedHashMap<>();
+            if (Objects.nonNull(body) && body.length > 0) {
+                String formBody = new String(body, StandardCharsets.UTF_8);
+                for (String pair : formBody.split("&")) {
+                    if (pair.isEmpty()) {
+                        continue;
+                    }
+                    int separator = pair.indexOf('=');
+                    String encodedName = separator < 0 ? pair : 
pair.substring(0, separator);
+                    String encodedValue = separator < 0 ? "" : 
pair.substring(separator + 1);
+                    String name = URLDecoder.decode(encodedName, 
StandardCharsets.UTF_8);
+                    String value = URLDecoder.decode(encodedValue, 
StandardCharsets.UTF_8);
+                    parsedForms.putIfAbsent(name, value);
+                }
+            }
+            forms = parsedForms;
+        }
+        return forms;
     }
 
     /**
diff --git 
a/shenyu-plugin/shenyu-plugin-mock/src/test/java/org/apache/shenyu/plugin/mock/api/MockRequestTest.java
 
b/shenyu-plugin/shenyu-plugin-mock/src/test/java/org/apache/shenyu/plugin/mock/api/MockRequestTest.java
new file mode 100644
index 0000000000..887fd88c13
--- /dev/null
+++ 
b/shenyu-plugin/shenyu-plugin-mock/src/test/java/org/apache/shenyu/plugin/mock/api/MockRequestTest.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.mock.api;
+
+import org.junit.jupiter.api.Test;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Tests for {@link MockRequest}.
+ */
+public final class MockRequestTest {
+
+    @Test
+    public void testGetFormsDecodesUtf8FormBody() {
+        MockRequest request = 
request("name=shenyu&message=hello+world&city=%E5%8C%97%E4%BA%AC&plus=%2B");
+
+        Map<String, String> forms = request.getForms();
+
+        assertEquals("shenyu", forms.get("name"));
+        assertEquals("hello world", forms.get("message"));
+        assertEquals("北京", forms.get("city"));
+        assertEquals("+", forms.get("plus"));
+    }
+
+    @Test
+    public void testGetFormsUsesFirstValueForDuplicateKeys() {
+        MockRequest request = request("name=first&name=second");
+
+        assertEquals("first", request.getForms().get("name"));
+    }
+
+    @Test
+    public void testGetFormsReturnsEmptyMapForEmptyOrMissingBody() {
+        MockRequest emptyBodyRequest = request("");
+        MockRequest missingBodyRequest = MockRequest.Builder.builder().build();
+
+        assertEquals(Map.of(), emptyBodyRequest.getForms());
+        assertEquals(Map.of(), missingBodyRequest.getForms());
+    }
+
+    @Test
+    public void testGetFormsCachesParsedMap() {
+        MockRequest request = request("name=shenyu");
+
+        assertSame(request.getForms(), request.getForms());
+    }
+
+    @Test
+    public void testGetFormsRejectsMalformedPercentEncoding() {
+        MockRequest request = request("name=%XX");
+
+        assertThrows(IllegalArgumentException.class, request::getForms);
+    }
+
+    private MockRequest request(final String body) {
+        return 
MockRequest.Builder.builder().body(body.getBytes(StandardCharsets.UTF_8)).build();
+    }
+}
diff --git 
a/shenyu-plugin/shenyu-plugin-mock/src/test/java/org/apache/shenyu/plugin/mock/generator/ExpressionGeneratorTest.java
 
b/shenyu-plugin/shenyu-plugin-mock/src/test/java/org/apache/shenyu/plugin/mock/generator/ExpressionGeneratorTest.java
index 4ef032b6b5..89583a4ce4 100644
--- 
a/shenyu-plugin/shenyu-plugin-mock/src/test/java/org/apache/shenyu/plugin/mock/generator/ExpressionGeneratorTest.java
+++ 
b/shenyu-plugin/shenyu-plugin-mock/src/test/java/org/apache/shenyu/plugin/mock/generator/ExpressionGeneratorTest.java
@@ -189,6 +189,16 @@ public class ExpressionGeneratorTest {
                 is("\"CHINA\""));
     }
 
+    @Test
+    public void testGenerateDataFromUrlEncodedForm() {
+        MockRequest formRequest = MockRequest.Builder.builder()
+                
.body("name=shenyu&message=hello+world".getBytes(StandardCharsets.UTF_8))
+                .build();
+
+        assertThat(generator.generate("expression|#req.forms['name']", 
formRequest), is("\"shenyu\""));
+        assertThat(generator.generate("expression|#req.forms.message", 
formRequest), is("\"hello world\""));
+    }
+
     @Test
     public void testGenerateDataFromReqConcurrently() throws Exception {
         ExecutorService executor = Executors.newFixedThreadPool(2);

Reply via email to