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 73bdd8eef4 fix(param-mapping): pass through empty JSON bodies (#7992)
73bdd8eef4 is described below

commit 73bdd8eef404d6715c73aa3af00faff433153e64
Author: Duansg <[email protected]>
AuthorDate: Thu Oct 22 19:04:17 2026 -0700

    fix(param-mapping): pass through empty JSON bodies (#7992)
---
 .../param/mapping/strategy/JsonOperator.java       | 35 +++++------
 .../mapping/strategy/FormDataOperatorTest.java     |  1 +
 .../param/mapping/strategy/JsonOperatorTest.java   | 67 +++++++++++++++++++++-
 3 files changed, 86 insertions(+), 17 deletions(-)

diff --git 
a/shenyu-plugin/shenyu-plugin-param-mapping/src/main/java/org/apache/shenyu/plugin/param/mapping/strategy/JsonOperator.java
 
b/shenyu-plugin/shenyu-plugin-param-mapping/src/main/java/org/apache/shenyu/plugin/param/mapping/strategy/JsonOperator.java
index 42b0c62d3c..fd21ad40c3 100644
--- 
a/shenyu-plugin/shenyu-plugin-param-mapping/src/main/java/org/apache/shenyu/plugin/param/mapping/strategy/JsonOperator.java
+++ 
b/shenyu-plugin/shenyu-plugin-param-mapping/src/main/java/org/apache/shenyu/plugin/param/mapping/strategy/JsonOperator.java
@@ -17,6 +17,7 @@
 
 package org.apache.shenyu.plugin.param.mapping.strategy;
 
+import org.apache.commons.collections4.CollectionUtils;
 import org.apache.shenyu.common.dto.convert.rule.impl.ParamMappingRuleHandle;
 import org.apache.shenyu.plugin.api.ShenyuPluginChain;
 import org.apache.shenyu.plugin.base.support.BodyInserterContext;
@@ -60,26 +61,28 @@ public class JsonOperator implements Operator {
     public Mono<Void> apply(final ServerWebExchange exchange, final 
ShenyuPluginChain shenyuPluginChain,
                             final ParamMappingRuleHandle 
paramMappingRuleHandle) {
         ServerRequest serverRequest = ServerRequest.create(exchange, 
messageReaders);
-        Mono<String> mono = 
serverRequest.bodyToMono(String.class).switchIfEmpty(Mono.just(""))
+        return serverRequest.bodyToMono(String.class).defaultIfEmpty("")
                 .flatMap(originalBody -> {
+                    if (originalBody.isEmpty() && 
CollectionUtils.isEmpty(paramMappingRuleHandle.getAddParameterKeys())) {
+                        return shenyuPluginChain.execute(exchange);
+                    }
                     LOG.info("get body data success data:{}", originalBody);
                     // process entity
-                    String modify = operation(originalBody, 
paramMappingRuleHandle);
-                    return Mono.just(modify);
+                    String modify = operation(originalBody.isEmpty() ? "{}" : 
originalBody, paramMappingRuleHandle);
+                    BodyInserter<Mono<String>, ReactiveHttpOutputMessage> 
bodyInserter = BodyInserters.fromPublisher(Mono.just(modify),
+                            String.class);
+                    HttpHeaders headers = new HttpHeaders();
+                    headers.putAll(exchange.getRequest().getHeaders());
+                    headers.remove(HttpHeaders.CONTENT_LENGTH);
+                    CachedBodyOutputMessage outputMessage = new 
CachedBodyOutputMessage(exchange, headers);
+                    return bodyInserter.insert(outputMessage, new 
BodyInserterContext())
+                            .then(Mono.defer(() -> {
+                                ServerHttpRequestDecorator decorator = new 
ModifyServerHttpRequestDecorator(headers,
+                                        exchange.getRequest(), outputMessage);
+                                return 
Mono.just(exchange.mutate().request(decorator).build());
+                            })).flatMap(shenyuPluginChain::execute)
+                            .onErrorResume(throwable -> release(outputMessage, 
throwable));
                 });
-        BodyInserter<Mono<String>, ReactiveHttpOutputMessage> bodyInserter = 
BodyInserters.fromPublisher(mono,
-                String.class);
-        HttpHeaders headers = new HttpHeaders();
-        headers.putAll(exchange.getRequest().getHeaders());
-        headers.remove(HttpHeaders.CONTENT_LENGTH);
-        CachedBodyOutputMessage outputMessage = new 
CachedBodyOutputMessage(exchange, headers);
-        return bodyInserter.insert(outputMessage, new BodyInserterContext())
-                .then(Mono.defer(() -> {
-                    ServerHttpRequestDecorator decorator = new 
ModifyServerHttpRequestDecorator(headers,
-                            exchange.getRequest(), outputMessage);
-                    return 
Mono.just(exchange.mutate().request(decorator).build());
-                })).flatMap(shenyuPluginChain::execute)
-                .onErrorResume(throwable -> release(outputMessage, throwable));
     }
     
     static class ModifyServerHttpRequestDecorator extends 
ServerHttpRequestDecorator {
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 fa612c176f..d708173e07 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
@@ -141,6 +141,7 @@ public class FormDataOperatorTest {
     @ParameterizedTest
     @CsvSource({
         "flag,flag&injected=yes",
+        "flag&name=x,flag&name=x&injected=yes",
         "flag=,flag=&injected=yes",
         "flag&flag=&flag=value,flag&flag=&flag=value&injected=yes",
         
"encoded%20key&name=hello+world,encoded+key&name=hello+world&injected=yes"
diff --git 
a/shenyu-plugin/shenyu-plugin-param-mapping/src/test/java/org/apache/shenyu/plugin/param/mapping/strategy/JsonOperatorTest.java
 
b/shenyu-plugin/shenyu-plugin-param-mapping/src/test/java/org/apache/shenyu/plugin/param/mapping/strategy/JsonOperatorTest.java
index 1dc9305cdb..a2204a6102 100644
--- 
a/shenyu-plugin/shenyu-plugin-param-mapping/src/test/java/org/apache/shenyu/plugin/param/mapping/strategy/JsonOperatorTest.java
+++ 
b/shenyu-plugin/shenyu-plugin-param-mapping/src/test/java/org/apache/shenyu/plugin/param/mapping/strategy/JsonOperatorTest.java
@@ -17,13 +17,19 @@
 
 package org.apache.shenyu.plugin.param.mapping.strategy;
 
+import com.jayway.jsonpath.DocumentContext;
+import com.jayway.jsonpath.JsonPath;
 import org.apache.shenyu.common.dto.convert.rule.impl.ParamMappingRuleHandle;
 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.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;
@@ -34,11 +40,17 @@ import org.springframework.web.server.ServerWebExchange;
 import reactor.core.publisher.Mono;
 import reactor.test.StepVerifier;
 
+import java.nio.charset.StandardCharsets;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 /**
@@ -80,8 +92,61 @@ public class JsonOperatorTest {
 
     @Test
     public void testApply() {
+        DocumentContext body = JsonPath.parse(applyAndReadBody(this.exchange, 
paramMappingRuleHandle));
+
+        assertEquals("SHENYU", body.read("$.webName"));
+        assertEquals("shenyu", body.read("$.realName"));
+        assertEquals(2, body.<Map<String, Object>>read("$").size());
+    }
+
+    @ParameterizedTest
+    @NullSource
+    @EmptySource
+    public void testEmptyBodyWithoutAdditionsPassesThrough(final 
List<ParamMappingRuleHandle.ParamMapInfo> additions) {
+        ServerWebExchange jsonExchange = 
MockServerWebExchange.from(MockServerHttpRequest.post("/json")
+                .contentType(MediaType.APPLICATION_JSON).build());
+        paramMappingRuleHandle.setAddParameterKeys(additions);
         when(this.chain.execute(any())).thenReturn(Mono.empty());
-        StepVerifier.create(jsonOperator.apply(this.exchange, this.chain, 
paramMappingRuleHandle)).expectSubscription().verifyComplete();
+
+        StepVerifier.create(jsonOperator.apply(jsonExchange, this.chain, 
paramMappingRuleHandle)).verifyComplete();
+
+        verify(this.chain).execute(jsonExchange);
+    }
+
+    @Test
+    public void testEmptyStringBodyWithoutAdditionsPassesThrough() {
+        ServerWebExchange jsonExchange = 
MockServerWebExchange.from(MockServerHttpRequest.post("/json")
+                .contentType(MediaType.APPLICATION_JSON).body(""));
+        paramMappingRuleHandle.setAddParameterKeys(Collections.emptyList());
+
+        assertEquals("", applyAndReadBody(jsonExchange, 
paramMappingRuleHandle));
+        verify(this.chain).execute(jsonExchange);
+    }
+
+    @Test
+    public void testAddParameterToEmptyBody() {
+        ServerWebExchange jsonExchange = 
MockServerWebExchange.from(MockServerHttpRequest.post("/json")
+                .contentType(MediaType.APPLICATION_JSON).build());
+        ParamMappingRuleHandle rule = new ParamMappingRuleHandle();
+        rule.setAddParameterKeys(paramMappingRuleHandle.getAddParameterKeys());
+
+        assertEquals("{\"webName\":\"SHENYU\"}", 
applyAndReadBody(jsonExchange, rule));
+    }
+
+    private String applyAndReadBody(final ServerWebExchange jsonExchange, 
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(jsonOperator.apply(jsonExchange, this.chain, 
rule)).verifyComplete();
+        return body.get();
     }
 }
 

Reply via email to