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 7b57fa4070 fix(plugin-param-mapping): apply additions to empty form 
requests. (#7755)
7b57fa4070 is described below

commit 7b57fa4070739e05594d3960f53e71b03de50004
Author: ocean <[email protected]>
AuthorDate: Mon Oct 19 09:38:50 2026 +0800

    fix(plugin-param-mapping): apply additions to empty form requests. (#7755)
---
 .../param/mapping/strategy/FormDataOperator.java   |  3 +-
 .../mapping/strategy/FormDataOperatorTest.java     | 86 ++++++++++++++++++++--
 2 files changed, 81 insertions(+), 8 deletions(-)

diff --git 
a/shenyu-plugin/shenyu-plugin-param-mapping/src/main/java/org/apache/shenyu/plugin/param/mapping/strategy/FormDataOperator.java
 
b/shenyu-plugin/shenyu-plugin-param-mapping/src/main/java/org/apache/shenyu/plugin/param/mapping/strategy/FormDataOperator.java
index 6cc21235d3..5dbc22c7ea 100644
--- 
a/shenyu-plugin/shenyu-plugin-param-mapping/src/main/java/org/apache/shenyu/plugin/param/mapping/strategy/FormDataOperator.java
+++ 
b/shenyu-plugin/shenyu-plugin-param-mapping/src/main/java/org/apache/shenyu/plugin/param/mapping/strategy/FormDataOperator.java
@@ -64,7 +64,8 @@ public class FormDataOperator implements Operator {
         return exchange.getFormData()
                 .switchIfEmpty(Mono.just(new LinkedMultiValueMap<>()))
                 .flatMap(multiValueMap -> {
-                    if (Objects.isNull(multiValueMap) || 
multiValueMap.isEmpty()) {
+                    if (Objects.isNull(multiValueMap)
+                            || (multiValueMap.isEmpty() && 
CollectionUtils.isEmpty(paramMappingRuleHandle.getAddParameterKeys()))) {
                         return shenyuPluginChain.execute(exchange);
                     }
                     String original = 
GsonUtils.getInstance().toJson(multiValueMap);
diff --git 
a/shenyu-plugin/shenyu-plugin-param-mapping/src/test/java/org/apache/shenyu/plugin/param/mapping/strategy/FormDataOperatorTest.java
 
b/shenyu-plugin/shenyu-plugin-param-mapping/src/test/java/org/apache/shenyu/plugin/param/mapping/strategy/FormDataOperatorTest.java
index 74b83513c6..b9ea21f7a8 100644
--- 
a/shenyu-plugin/shenyu-plugin-param-mapping/src/test/java/org/apache/shenyu/plugin/param/mapping/strategy/FormDataOperatorTest.java
+++ 
b/shenyu-plugin/shenyu-plugin-param-mapping/src/test/java/org/apache/shenyu/plugin/param/mapping/strategy/FormDataOperatorTest.java
@@ -22,24 +22,36 @@ import org.apache.shenyu.plugin.api.ShenyuPluginChain;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EmptySource;
+import org.junit.jupiter.params.provider.NullSource;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.core.io.buffer.DataBufferUtils;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpMethod;
 import org.springframework.http.MediaType;
 import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
 import org.springframework.mock.web.server.MockServerWebExchange;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
 import org.springframework.web.server.ServerWebExchange;
 import reactor.core.publisher.Mono;
 import reactor.test.StepVerifier;
 
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 
@@ -70,21 +82,81 @@ public class FormDataOperatorTest {
         replace.setPath("$");
         replace.setKey("name");
         replace.setValue("realName");
-        MultiValueMap<String, String> param = new LinkedMultiValueMap<>();
-        param.set("name", "shenyu");
-        param.set("age", "18");
         this.paramMappingRuleHandle = new ParamMappingRuleHandle();
         this.paramMappingRuleHandle.setRemoveParameterKeys(remove);
         
this.paramMappingRuleHandle.setAddParameterKeys(Collections.singletonList(add));
         
this.paramMappingRuleHandle.setReplaceParameterKeys(Collections.singletonList(replace));
         this.formDataOperator = new FormDataOperator();
         this.exchange = 
MockServerWebExchange.from(MockServerHttpRequest.method(HttpMethod.POST, 
"localhost")
-                .header(HttpHeaders.CONTENT_TYPE, 
MediaType.APPLICATION_FORM_URLENCODED_VALUE).queryParams(param));
+                .header(HttpHeaders.CONTENT_TYPE, 
MediaType.APPLICATION_FORM_URLENCODED_VALUE).body("name=shenyu&age=18"));
     }
 
     @Test
     public void testApply() {
+        String body = applyAndReadBody(this.exchange, paramMappingRuleHandle);
+        assertTrue(body.contains("webName=SHENYU"));
+        assertTrue(body.contains("realName=shenyu"));
+        assertFalse(body.contains("age="));
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = {"yes", "中文 &+"})
+    public void testAddParameterToEmptyForm(final String value) {
+        ServerWebExchange formExchange = 
MockServerWebExchange.from(MockServerHttpRequest.post("/form")
+                .contentType(MediaType.APPLICATION_FORM_URLENCODED).body(""));
+
+        String body = applyAndReadBody(formExchange, additionRule(value));
+        assertEquals("injected=" + URLEncoder.encode(value, 
StandardCharsets.UTF_8), body);
+        ArgumentCaptor<ServerWebExchange> downstream = 
ArgumentCaptor.forClass(ServerWebExchange.class);
+        verify(this.chain).execute(downstream.capture());
+        assertEquals(body.getBytes(StandardCharsets.UTF_8).length, 
downstream.getValue().getRequest().getHeaders().getContentLength());
+    }
+
+    @Test
+    public void testAddParameterToNonEmptyForm() {
+        ServerWebExchange formExchange = 
MockServerWebExchange.from(MockServerHttpRequest.post("/form")
+                
.contentType(MediaType.APPLICATION_FORM_URLENCODED).body("a=1"));
+
+        assertEquals("a=1&injected=yes", applyAndReadBody(formExchange, 
additionRule("yes")));
+    }
+
+    @ParameterizedTest
+    @NullSource
+    @EmptySource
+    public void testEmptyFormWithoutAdditionsPassesThrough(final 
List<ParamMappingRuleHandle.ParamMapInfo> additions) {
+        ServerWebExchange formExchange = 
MockServerWebExchange.from(MockServerHttpRequest.post("/form")
+                .contentType(MediaType.APPLICATION_FORM_URLENCODED).body(""));
+        paramMappingRuleHandle.setAddParameterKeys(additions);
         when(this.chain.execute(any())).thenReturn(Mono.empty());
-        StepVerifier.create(formDataOperator.apply(this.exchange, this.chain, 
paramMappingRuleHandle)).expectSubscription().verifyComplete();
+
+        StepVerifier.create(formDataOperator.apply(formExchange, this.chain, 
paramMappingRuleHandle)).verifyComplete();
+
+        verify(this.chain).execute(formExchange);
+    }
+
+    private ParamMappingRuleHandle additionRule(final String value) {
+        ParamMappingRuleHandle.ParamMapInfo add = new 
ParamMappingRuleHandle.ParamMapInfo();
+        add.setPath("$");
+        add.setKey("injected");
+        add.setValue(value);
+        ParamMappingRuleHandle rule = new ParamMappingRuleHandle();
+        rule.setAddParameterKeys(Collections.singletonList(add));
+        return rule;
+    }
+
+    private String applyAndReadBody(final ServerWebExchange formExchange, 
final ParamMappingRuleHandle rule) {
+        AtomicReference<String> body = new AtomicReference<>("");
+        when(this.chain.execute(any())).thenAnswer(invocation -> {
+            ServerWebExchange downstream = invocation.getArgument(0);
+            return 
DataBufferUtils.join(downstream.getRequest().getBody()).doOnNext(buffer -> {
+                try {
+                    body.set(buffer.toString(StandardCharsets.UTF_8));
+                } finally {
+                    DataBufferUtils.release(buffer);
+                }
+            }).then();
+        });
+        StepVerifier.create(formDataOperator.apply(formExchange, this.chain, 
rule)).verifyComplete();
+        return body.get();
     }
 }

Reply via email to