Calls RewriteFunction even if body is empty.

fixes gh-1219
fixes gh-1220
This commit is contained in:
Stefan_Stus
2019-07-31 22:42:43 +03:00
committed by Spencer Gibb
parent 2e890864bd
commit 7eaecedd5e
6 changed files with 167 additions and 15 deletions

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

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

View File

@@ -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")

View File

@@ -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() {