Rewrite response header filter factory.

This commit is contained in:
Vitaliy Pavlyuk
2018-10-04 20:49:16 -04:00
committed by Spencer Gibb
parent 090addc8de
commit 5ca54b6eca
7 changed files with 188 additions and 1 deletions

View File

@@ -703,6 +703,24 @@ spring:
For a request path of `/foo/bar`, this will set the path to `/bar` before making the downstream request. Notice the `$\` which is replaced with `$` because of the YAML spec.
=== RewriteResponseHeader GatewayFilter Factory
The RewriteResponseHeader GatewayFilter Factory takes `name`, `regexp`, and `replacement` parameters. It uses Java regular expressions for a flexible way to rewrite the response header value.
.application.yml
[source,yaml]
----
spring:
cloud:
gateway:
routes:
- id: rewriteresponseheader_route
uri: http://example.org
filters:
- RewriteResponseHeader=X-Response-Foo, , password=[^&]+, password=***
----
For a header value of `/42?user=ford&password=omg!what&flag=true`, it will be set to `/42?user=ford&password=\***&flag=true` before making the downstream request. Please use `$\` to mean `$` because of the YAML spec.
=== SaveSession GatewayFilter Factory
The SaveSession GatewayFilter Factory forces a `WebSession::save` operation _before_ forwarding the call downstream. This is of particular use when
using something like http://projects.spring.io/spring-session/[Spring Session] with a lazy data store and need to ensure the session state has been saved before making the forwarded call.

View File

@@ -70,6 +70,7 @@ import org.springframework.cloud.gateway.filter.factory.RequestRateLimiterGatewa
import org.springframework.cloud.gateway.filter.factory.RequestSizeGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.RetryGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.RewritePathGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.RewriteResponseHeaderGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.SaveSessionGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.SecureHeadersGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.SecureHeadersProperties;
@@ -585,6 +586,11 @@ public class GatewayAutoConfiguration {
return new SetResponseHeaderGatewayFilterFactory();
}
@Bean
public RewriteResponseHeaderGatewayFilterFactory rewriteResponseHeaderGatewayFilterFactory() {
return new RewriteResponseHeaderGatewayFilterFactory();
}
@Bean
public SetStatusGatewayFilterFactory setStatusGatewayFilterFactory() {
return new SetStatusGatewayFilterFactory();

View File

@@ -0,0 +1,87 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.gateway.filter.factory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.List;
/**
* @author Vitaliy Pavlyuk
*/
public class RewriteResponseHeaderGatewayFilterFactory extends AbstractGatewayFilterFactory<RewriteResponseHeaderGatewayFilterFactory.Config> {
public static final String REGEXP_KEY = "regexp";
public static final String REPLACEMENT_KEY = "replacement";
public RewriteResponseHeaderGatewayFilterFactory() {
super(Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList(NAME_KEY, REGEXP_KEY, REPLACEMENT_KEY);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> chain.filter(exchange).then(Mono.fromRunnable(() -> {
rewriteHeader(exchange, config);
}));
}
protected void rewriteHeader(ServerWebExchange exchange, Config config) {
final String name = config.getName();
final String value = exchange.getResponse().getHeaders().getFirst(name);
if (value == null) {
return;
}
final String newValue = rewrite(value, config.getRegexp(), config.getReplacement());
exchange.getResponse().getHeaders().set(name, newValue);
}
String rewrite(String value, String regexp, String replacement) {
return value.replaceAll(regexp, replacement.replace("$\\", "$"));
}
public static class Config extends AbstractGatewayFilterFactory.NameConfig {
private String regexp;
private String replacement;
public String getRegexp() {
return regexp;
}
public Config setRegexp(String regexp) {
this.regexp = regexp;
return this;
}
public String getReplacement() {
return replacement;
}
public Config setReplacement(String replacement) {
this.replacement = replacement;
return this;
}
}
}

View File

@@ -51,6 +51,7 @@ import org.springframework.cloud.gateway.filter.factory.RequestRateLimiterGatewa
import org.springframework.cloud.gateway.filter.factory.RequestSizeGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.RetryGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.RewritePathGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.RewriteResponseHeaderGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.SaveSessionGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.SecureHeadersGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.SetPathGatewayFilterFactory;
@@ -477,6 +478,18 @@ public class GatewayFilterSpec extends UriSpec {
.apply(c -> c.setName(headerName).setValue(headerValue)));
}
/**
* A filter that rewrites a header value on the response before it is returned to the client by the Gateway.
* @param headerName the header name
* @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
*/
public GatewayFilterSpec rewriteResponseHeader(String headerName, String regex, String replacement) {
return filter(getBean(RewriteResponseHeaderGatewayFilterFactory.class)
.apply(c -> c.setReplacement(replacement).setRegexp(regex).setName(headerName)));
}
/**
* A filter that sets the status on the response before it is returned to the client by the Gateway.
* @param status the status to set on the response
@@ -557,7 +570,7 @@ public class GatewayFilterSpec extends UriSpec {
}.apply(c -> {
}));
}
/**
* A filter that sets the maximum permissible size of a Request.

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.gateway.filter.factory;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.test.BaseWebClientTests;
import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = RANDOM_PORT)
@DirtiesContext
public class RewriteResponseHeaderGatewayFilterFactoryTests extends BaseWebClientTests {
@Test
public void rewriteResponseHeaderFilterWorks() {
testClient.get()
.uri("/headers")
.header("Host", "www.rewriteresponseheader.org")
.exchange()
.expectStatus().isOk()
.expectHeader().valueEquals("X-Request-Foo", "/42?user=ford&password=***&flag=true");
}
@EnableAutoConfiguration
@SpringBootConfiguration
@Import(DefaultTestConfig.class)
public static class TestConfig { }
}

View File

@@ -76,6 +76,7 @@ import static org.junit.Assume.assumeThat;
org.springframework.cloud.gateway.filter.factory.RewritePathGatewayFilterFactoryIntegrationTests.class,
org.springframework.cloud.gateway.filter.factory.AddRequestHeaderGatewayFilterFactoryTests.class,
org.springframework.cloud.gateway.filter.factory.SetResponseHeaderGatewayFilterFactoryTests.class,
org.springframework.cloud.gateway.filter.factory.RewriteResponseHeaderGatewayFilterFactoryTests.class,
org.springframework.cloud.gateway.filter.WeightCalculatorWebFilterTests.class,
org.springframework.cloud.gateway.filter.RouteToRequestUrlFilterTests.class,
org.springframework.cloud.gateway.filter.headers.ForwardedHeadersFilterTests.class,

View File

@@ -65,6 +65,16 @@ spring:
filters:
- AddResponseHeader=X-Request-Foo, Bar
# =====================================
- id: rewrite_response_header_test
uri: ${test.uri}
predicates:
- Host=**.rewriteresponseheader.org
- Path=/headers
filters:
- AddResponseHeader=X-Request-Foo, /42?user=ford&password=omg!what&flag=true
- RewriteResponseHeader=X-Request-Foo, password=[^&]+, password=***
# =====================================
- id: forward_test
uri: forward:/localcontroller