Calls RewriteFunction even if body is empty.
fixes gh-1219 fixes gh-1220
This commit is contained in:
committed by
Spencer Gibb
parent
2e890864bd
commit
7eaecedd5e
@@ -203,8 +203,10 @@ public class ModifyResponseBodyGatewayFilterFactory extends
|
||||
|
||||
// TODO: flux or mono
|
||||
Mono modifiedBody = clientResponse.bodyToMono(inClass)
|
||||
.flatMap(originalBody -> config.rewriteFunction
|
||||
.apply(exchange, originalBody));
|
||||
.flatMap(originalBody -> config.getRewriteFunction()
|
||||
.apply(exchange, originalBody))
|
||||
.switchIfEmpty(Mono.defer(() -> (Mono) config
|
||||
.getRewriteFunction().apply(exchange, null)));
|
||||
|
||||
BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody,
|
||||
outClass);
|
||||
|
||||
@@ -246,8 +246,7 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
}
|
||||
|
||||
/**
|
||||
* A filter that can be used to modify the request body. This filter is BETA and may
|
||||
* be subject to change in a future release.
|
||||
* A filter that can be used to modify the request body.
|
||||
* @param inClass the class to convert the incoming request body to
|
||||
* @param outClass the class the Gateway will add to the request before it is routed
|
||||
* @param rewriteFunction the {@link RewriteFunction} that transforms the request body
|
||||
@@ -263,8 +262,7 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
}
|
||||
|
||||
/**
|
||||
* A filter that can be used to modify the request body. This filter is BETA and may
|
||||
* be subject to change in a future release.
|
||||
* A filter that can be used to modify the request body.
|
||||
* @param inClass the class to convert the incoming request body to
|
||||
* @param outClass the class the Gateway will add to the request before it is routed
|
||||
* @param newContentType the new Content-Type header to be sent
|
||||
@@ -281,9 +279,10 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
}
|
||||
|
||||
/**
|
||||
* A filter that can be used to modify the request body. This filter is BETA and may
|
||||
* be subject to change in a future release.
|
||||
* A filter that can be used to modify the request body.
|
||||
* @param configConsumer request spec for response modification
|
||||
* @param <T> the original request body class
|
||||
* @param <R> the new request body class
|
||||
* @return a {@link GatewayFilterSpec} that can be used to apply additional filters
|
||||
* <pre>
|
||||
* {@code
|
||||
@@ -304,8 +303,7 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
}
|
||||
|
||||
/**
|
||||
* A filter that can be used to modify the response body This filter is BETA and may
|
||||
* be subject to change in a future release.
|
||||
* A filter that can be used to modify the response body.
|
||||
* @param inClass the class to conver the response body to
|
||||
* @param outClass the class the Gateway will add to the response before it is
|
||||
* returned to the client
|
||||
@@ -322,8 +320,7 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
}
|
||||
|
||||
/**
|
||||
* A filter that can be used to modify the response body This filter is BETA and may
|
||||
* be subject to change in a future release.
|
||||
* A filter that can be used to modify the response body.
|
||||
* @param inClass the class to conver the response body to
|
||||
* @param outClass the class the Gateway will add to the response before it is
|
||||
* returned to the client
|
||||
@@ -344,9 +341,10 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
}
|
||||
|
||||
/**
|
||||
* A filter that can be used to modify the response body using custom spec. This
|
||||
* filter is BETA and may be subject to change in a future release.
|
||||
* A filter that can be used to modify the response body using custom spec.
|
||||
* @param configConsumer response spec for response modification
|
||||
* @param <T> the original response body class
|
||||
* @param <R> the new response body class
|
||||
* @return a {@link GatewayFilterSpec} that can be used to apply additional filters
|
||||
* <pre>
|
||||
* {@code
|
||||
@@ -501,7 +499,7 @@ public class GatewayFilterSpec extends UriSpec {
|
||||
}
|
||||
|
||||
/**
|
||||
* A filter which rewrites the request path before it is routed by the Gateway
|
||||
* A filter which rewrites the request path before it is routed by the Gateway.
|
||||
* @param regex a Java regular expression to match the path against
|
||||
* @param replacement the replacement for the path
|
||||
* @return a {@link GatewayFilterSpec} that can be used to apply additional filters
|
||||
|
||||
@@ -21,14 +21,18 @@ import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
|
||||
import org.springframework.cloud.gateway.filter.OrderedGatewayFilter;
|
||||
import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyResponseBodyGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.route.Route;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class GatewayFilterSpecTests {
|
||||
|
||||
@@ -81,6 +85,100 @@ public class GatewayFilterSpecTests {
|
||||
assertFilter(route.getFilters().get(1), MyOrderedFilter.class, 1000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSetModifyBodyResponseFilterWithRewriteFunction() {
|
||||
ConfigurableApplicationContext context = mock(
|
||||
ConfigurableApplicationContext.class);
|
||||
Route.AsyncBuilder routeBuilder = Route.async().id("123").uri("abc:123")
|
||||
.predicate(exchange -> true);
|
||||
|
||||
when(context.getBean(ModifyResponseBodyGatewayFilterFactory.class))
|
||||
.thenReturn(new ModifyResponseBodyGatewayFilterFactory());
|
||||
|
||||
RouteLocatorBuilder.Builder routes = new RouteLocatorBuilder(context).routes();
|
||||
GatewayFilterSpec spec = new GatewayFilterSpec(routeBuilder, routes);
|
||||
spec.modifyResponseBody(String.class, String.class,
|
||||
(exchange, s) -> Mono.just(s));
|
||||
|
||||
Route route = routeBuilder.build();
|
||||
assertThat(route.getFilters()).hasSize(1);
|
||||
|
||||
assertFilter(route.getFilters().get(0),
|
||||
ModifyResponseBodyGatewayFilterFactory.ModifyResponseGatewayFilter.class,
|
||||
NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSetModifyBodyResponseFilterWithRewriteFunctionAndEmptyBodySupplier() {
|
||||
ConfigurableApplicationContext context = mock(
|
||||
ConfigurableApplicationContext.class);
|
||||
Route.AsyncBuilder routeBuilder = Route.async().id("123").uri("abc:123")
|
||||
.predicate(exchange -> true);
|
||||
|
||||
when(context.getBean(ModifyResponseBodyGatewayFilterFactory.class))
|
||||
.thenReturn(new ModifyResponseBodyGatewayFilterFactory());
|
||||
|
||||
RouteLocatorBuilder.Builder routes = new RouteLocatorBuilder(context).routes();
|
||||
GatewayFilterSpec spec = new GatewayFilterSpec(routeBuilder, routes);
|
||||
spec.modifyResponseBody(String.class, String.class,
|
||||
(exchange, s) -> Mono.just(s == null ? "emptybody" : s));
|
||||
|
||||
Route route = routeBuilder.build();
|
||||
assertThat(route.getFilters()).hasSize(1);
|
||||
|
||||
assertFilter(route.getFilters().get(0),
|
||||
ModifyResponseBodyGatewayFilterFactory.ModifyResponseGatewayFilter.class,
|
||||
NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSetModifyBodyResponseFilterWithRewriteFunctionAndNewContentType() {
|
||||
ConfigurableApplicationContext context = mock(
|
||||
ConfigurableApplicationContext.class);
|
||||
Route.AsyncBuilder routeBuilder = Route.async().id("123").uri("abc:123")
|
||||
.predicate(exchange -> true);
|
||||
|
||||
when(context.getBean(ModifyResponseBodyGatewayFilterFactory.class))
|
||||
.thenReturn(new ModifyResponseBodyGatewayFilterFactory());
|
||||
|
||||
RouteLocatorBuilder.Builder routes = new RouteLocatorBuilder(context).routes();
|
||||
GatewayFilterSpec spec = new GatewayFilterSpec(routeBuilder, routes);
|
||||
spec.modifyResponseBody(String.class, String.class,
|
||||
MediaType.APPLICATION_JSON_VALUE, (exchange, s) -> Mono.just(s));
|
||||
|
||||
Route route = routeBuilder.build();
|
||||
assertThat(route.getFilters()).hasSize(1);
|
||||
|
||||
assertFilter(route.getFilters().get(0),
|
||||
ModifyResponseBodyGatewayFilterFactory.ModifyResponseGatewayFilter.class,
|
||||
NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSetModifyBodyResponseFilterWithConfigConsumer() {
|
||||
ConfigurableApplicationContext context = mock(
|
||||
ConfigurableApplicationContext.class);
|
||||
Route.AsyncBuilder routeBuilder = Route.async().id("123").uri("abc:123")
|
||||
.predicate(exchange -> true);
|
||||
|
||||
when(context.getBean(ModifyResponseBodyGatewayFilterFactory.class))
|
||||
.thenReturn(new ModifyResponseBodyGatewayFilterFactory());
|
||||
|
||||
RouteLocatorBuilder.Builder routes = new RouteLocatorBuilder(context).routes();
|
||||
GatewayFilterSpec spec = new GatewayFilterSpec(routeBuilder, routes);
|
||||
spec.modifyResponseBody(
|
||||
(smth) -> new ModifyResponseBodyGatewayFilterFactory.Config()
|
||||
.setRewriteFunction(String.class, String.class,
|
||||
(exchange, s) -> Mono.just(s)));
|
||||
|
||||
Route route = routeBuilder.build();
|
||||
assertThat(route.getFilters()).hasSize(1);
|
||||
|
||||
assertFilter(route.getFilters().get(0),
|
||||
ModifyResponseBodyGatewayFilterFactory.ModifyResponseGatewayFilter.class,
|
||||
NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1);
|
||||
}
|
||||
|
||||
protected static class MyOrderedFilter implements GatewayFilter, Ordered {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -147,6 +147,12 @@ public class HttpBinCompatibleController {
|
||||
return ResponseEntity.status(status).body("Failed with " + status);
|
||||
}
|
||||
|
||||
@RequestMapping(path = "/post/empty", method = RequestMethod.POST,
|
||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public Mono<String> emptyResponse() {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
public Map<String, String> getHeaders(ServerWebExchange exchange) {
|
||||
return exchange.getRequest().getHeaders().toSingleValueMap();
|
||||
}
|
||||
|
||||
@@ -99,6 +99,31 @@ public class GatewaySampleApplication {
|
||||
})
|
||||
).uri(uri)
|
||||
)
|
||||
.route("rewrite_empty_response", r -> r.host("*.rewriteemptyresponse.org")
|
||||
.filters(f -> f.prefixPath("/httpbin")
|
||||
.addResponseHeader("X-TestHeader", "rewrite_empty_response")
|
||||
.modifyResponseBody(String.class, String.class,
|
||||
(exchange, s) -> {
|
||||
if (s == null) {
|
||||
return Mono.just("emptybody");
|
||||
}
|
||||
return Mono.just(s.toUpperCase());
|
||||
})
|
||||
|
||||
).uri(uri)
|
||||
)
|
||||
.route("rewrite_response_fail_supplier", r -> r.host("*.rewriteresponsewithfailsupplier.org")
|
||||
.filters(f -> f.prefixPath("/httpbin")
|
||||
.addResponseHeader("X-TestHeader", "rewrite_response_fail_supplier")
|
||||
.modifyResponseBody(String.class, String.class,
|
||||
(exchange, s) -> {
|
||||
if (s == null) {
|
||||
return Mono.error(new IllegalArgumentException("this should not happen"));
|
||||
}
|
||||
return Mono.just(s.toUpperCase());
|
||||
})
|
||||
).uri(uri)
|
||||
)
|
||||
.route("rewrite_response_obj", r -> r.host("*.rewriteresponseobj.org")
|
||||
.filters(f -> f.prefixPath("/httpbin")
|
||||
.addResponseHeader("X-TestHeader", "rewrite_response_obj")
|
||||
|
||||
@@ -130,6 +130,29 @@ public class GatewaySampleApplicationTests {
|
||||
.containsEntry("DATA", "HELLO"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void rewriteResponseEmptyBodyToStringWorks() {
|
||||
webClient.post().uri("/post/empty").header("Host", "www.rewriteemptyresponse.org")
|
||||
.exchange().expectStatus().isOk().expectHeader()
|
||||
.valueEquals("X-TestHeader", "rewrite_empty_response")
|
||||
.expectBody(String.class)
|
||||
.consumeWith(result -> assertThat(result.getResponseBody())
|
||||
.isEqualTo("emptybody"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void emptyBodySupplierNotCalledWhenBodyPresent() {
|
||||
webClient.post().uri("/post")
|
||||
.header("Host", "www.rewriteresponsewithfailsupplier.org")
|
||||
.bodyValue("hello").exchange().expectStatus().isOk().expectHeader()
|
||||
.valueEquals("X-TestHeader", "rewrite_response_fail_supplier")
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(result -> assertThat(result.getResponseBody())
|
||||
.containsEntry("DATA", "HELLO"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void rewriteResponeBodyObjectWorks() {
|
||||
|
||||
Reference in New Issue
Block a user