add new config for AddResponseHeaderGatewayFilterFactory

Signed-off-by: jiangyuan <joe469391363@gmail.com>
This commit is contained in:
jiangyuan
2025-05-19 17:23:20 +08:00
parent 6fc7ee7ecd
commit ca861ff4c8
4 changed files with 154 additions and 15 deletions

View File

@@ -16,12 +16,17 @@
package org.springframework.cloud.gateway.filter.factory;
import java.util.Arrays;
import java.util.List;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.style.ToStringCreator;
import org.springframework.http.HttpHeaders;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
@@ -29,10 +34,22 @@ import static org.springframework.cloud.gateway.support.GatewayToStringStyler.fi
/**
* @author Spencer Gibb
*/
public class AddResponseHeaderGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
public class AddResponseHeaderGatewayFilterFactory
extends AbstractGatewayFilterFactory<AddResponseHeaderGatewayFilterFactory.Config> {
private static final String OVERRIDE_KEY = "override";
public AddResponseHeaderGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(NameValueConfig config) {
public List<String> shortcutFieldOrder() {
return Arrays.asList(GatewayFilter.NAME_KEY, GatewayFilter.VALUE_KEY, OVERRIDE_KEY);
}
@Override
public GatewayFilter apply(Config config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
@@ -42,19 +59,76 @@ public class AddResponseHeaderGatewayFilterFactory extends AbstractNameValueGate
@Override
public String toString() {
return filterToStringCreator(AddResponseHeaderGatewayFilterFactory.this)
.append(config.getName(), config.getValue())
.append(GatewayFilter.NAME_KEY, config.getName())
.append(GatewayFilter.VALUE_KEY, config.getValue())
.append(OVERRIDE_KEY, config.isOverride())
.toString();
}
};
}
void addHeader(ServerWebExchange exchange, NameValueConfig config) {
final String value = ServerWebExchangeUtils.expand(exchange, config.getValue());
HttpHeaders headers = exchange.getResponse().getHeaders();
void addHeader(ServerWebExchange exchange, Config config) {
// if response has been commited, no more response headers will bee added.
if (!exchange.getResponse().isCommitted()) {
headers.add(config.getName(), value);
final String value = ServerWebExchangeUtils.expand(exchange, config.getValue());
HttpHeaders headers = exchange.getResponse().getHeaders();
if (config.override) {
headers.add(config.getName(), value);
}
else {
boolean headerIsMissingOrBlank = headers.getOrEmpty(config.getName())
.stream()
.allMatch(h -> !StringUtils.hasText(h));
if (headerIsMissingOrBlank) {
headers.add(config.getName(), value);
}
}
}
}
public static class Config {
private String name;
private String value;
private boolean override = true;
public String getName() {
return name;
}
public Config setName(String name) {
this.name = name;
return this;
}
public String getValue() {
return value;
}
public Config setValue(String value) {
this.value = value;
return this;
}
public boolean isOverride() {
return override;
}
public Config setOverride(boolean override) {
this.override = override;
return this;
}
@Override
public String toString() {
return new ToStringCreator(this).append(NAME_KEY, name)
.append(VALUE_KEY, value)
.append(OVERRIDE_KEY, override)
.toString();
}
}
}

View File

@@ -225,6 +225,18 @@ public class GatewayFilterSpec extends UriSpec {
.apply(c -> c.setName(headerName).setValue(headerValue)));
}
/**
* Adds a header to the response returned to the Gateway from the route.
* @param headerName the header name
* @param headerValue the header value
* @param override override or not
* @return a {@link GatewayFilterSpec} that can be used to apply additional filters
*/
public GatewayFilterSpec addResponseHeader(String headerName, String headerValue, boolean override) {
return filter(getBean(AddResponseHeaderGatewayFilterFactory.class)
.apply(c -> c.setName(headerName).setValue(headerValue).setOverride(override)));
}
/**
* A filter that adds a local cache for storing response body for repeated requests.
* <p>

View File

@@ -17,6 +17,8 @@
package org.springframework.cloud.gateway.filter.factory;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.Test;
@@ -25,7 +27,6 @@ import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractNameValueGatewayFilterFactory.NameValueConfig;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.test.BaseWebClientTests;
@@ -51,20 +52,71 @@ class AddResponseHeaderGatewayFilterFactoryTests extends BaseWebClientTests {
.header("Host", host)
.exchange()
.expectHeader()
.valueEquals("X-Request-Foo", expectedValue);
.valueEquals("X-Request-Foo", expectedValue)
.expectHeader()
.valueEquals("X-Request-Example", "ValueA");
}
@Test
void testResponseHeaderFilterHeaderPresent() {
URI uri = UriComponentsBuilder.fromUriString(this.baseUri + "/headers").build(true).toUri();
String host = "www.addresponseheader.org";
String expectedValue = "Bar";
Map<String, String> body = new HashMap<>();
body.put("X-Request-Example", "ValueB");
testClient.patch()
.uri(uri)
.header("Host", host)
.bodyValue(body)
.exchange()
.expectHeader()
.valueEquals("X-Request-Foo", expectedValue)
.expectHeader()
.valueEquals("X-Request-Example", "ValueB");
}
@Test
void testResponseHeaderFilterJavaDsl() {
URI uri = UriComponentsBuilder.fromUriString(this.baseUri + "/get").build(true).toUri();
URI uri = UriComponentsBuilder.fromUriString(this.baseUri + "/headers").build(true).toUri();
String host = "www.addresponseheaderjava.org";
String expectedValue = "myresponsevalue-www";
testClient.get().uri(uri).header("Host", host).exchange().expectHeader().valueEquals("example", expectedValue);
testClient.get()
.uri(uri)
.header("Host", host)
.exchange()
.expectHeader()
.valueEquals("example", expectedValue)
.expectHeader()
.valueEquals("example2", "myresponsevalue2-www");
}
@Test
void testResponseHeaderFilterHeaderPresentJavaDsl() {
URI uri = UriComponentsBuilder.fromUriString(this.baseUri + "/headers").build(true).toUri();
String host = "www.addresponseheaderjava.org";
String expectedValue = "myresponsevalue-www";
Map<String, String> body = new HashMap<>();
body.put("example2", "myresponsevalue2");
testClient.patch()
.uri(uri)
.header("Host", host)
.bodyValue(body)
.exchange()
.expectHeader()
.valueEquals("example", expectedValue)
.expectHeader()
.valueEquals("example2", "myresponsevalue2");
}
@Test
void toStringFormat() {
NameValueConfig config = new NameValueConfig().setName("myname").setValue("myvalue");
AddResponseHeaderGatewayFilterFactory.Config config = new AddResponseHeaderGatewayFilterFactory.Config()
.setName("myname")
.setValue("myvalue");
GatewayFilter filter = new AddResponseHeaderGatewayFilterFactory().apply(config);
assertThat(filter.toString()).contains("myname").contains("myvalue");
}
@@ -81,11 +133,11 @@ class AddResponseHeaderGatewayFilterFactoryTests extends BaseWebClientTests {
public RouteLocator testRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("add_response_header_java_test",
r -> r.path("/get")
r -> r.path("/headers")
.and()
.host("{sub}.addresponseheaderjava.org")
.filters(
f -> f.prefixPath("/httpbin").addResponseHeader("example", "myresponsevalue-{sub}"))
.filters(f -> f.addResponseHeader("example", "myresponsevalue-{sub}")
.addResponseHeader("example2", "myresponsevalue2-{sub}", false))
.uri(uri))
.build();
}

View File

@@ -72,6 +72,7 @@ spring:
- Path=/headers
filters:
- AddResponseHeader=X-Request-Foo, Bar
- AddResponseHeader=X-Request-Example, ValueA, false
- id: cache_request_body_test
uri: ${test.uri}