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();
}
}