Order
This article mainly studies the GatewayFilter of spring cloud gateway.
GatewayFilter
spring-cloud-gateway-core-2.0.0.RC2-sources.jar! /org/springframework/cloud/gateway/filter/GatewayFilter.java
/**
* Contract for interception-style, chained processing of Web requests that may
* be used to implement cross-cutting, application-agnostic requirements such
* as security, timeouts, and others. Specific to a Gateway
*
* Copied from WebFilter
*
* @author Rossen Stoyanchev
* @since 5.0
*/
public interface GatewayFilter extends ShortcutConfigurable {
String NAME_KEY = "name";
String VALUE_KEY = "value";
/**
* Process the Web request and (optionally) delegate to the next
* {@code WebFilter} through the given {@link GatewayFilterChain}.
* @param exchange the current server exchange
* @param chain provides a way to delegate to the next filter
* @return {@code Mono<Void>} to indicate when request processing is complete
*/
Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}
As mentioned in the note, it is mainly used for non-functional requirements like security and timeout control. Its direct implementation classes are OrderedGatewayFilter, ModifyResponseGatewayFilter, GatewayFilterAdapter
OrderedGatewayFilter
spring-cloud-gateway-core-2.0.0.RC2-sources.jar! /org/springframework/cloud/gateway/filter/OrderedGatewayFilter.java
public class OrderedGatewayFilter implements GatewayFilter, Ordered {
private final GatewayFilter delegate;
private final int order;
public OrderedGatewayFilter(GatewayFilter delegate, int order) {
this.delegate = delegate;
this.order = order;
}
public GatewayFilter getDelegate() {
return delegate;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return this.delegate.filter(exchange, chain);
}
@Override
public int getOrder() {
return this.order;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("OrderedGatewayFilter{");
sb.append("delegate=").append(delegate);
sb.append(", order=").append(order);
sb.append('}');
return sb.toString();
}
}
The Order interface is implemented.
ModifyResponseGatewayFilter
spring-cloud-gateway-core-2.0.0.RC2-sources.jar! /org/springframework/cloud/gateway/filter/factory/rewrite/ModifyResponseBodyGatewayFilterFactory.java
public class ModifyResponseGatewayFilter implements GatewayFilter, Ordered {
private final Config config;
public ModifyResponseGatewayFilter(Config config) {
this.config = config;
}
@Override
@SuppressWarnings("unchecked")
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpResponseDecorator responseDecorator = new ServerHttpResponseDecorator(exchange.getResponse()) {
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
ResolvableType inElementType = ResolvableType.forClass(config.getInClass());
ResolvableType outElementType = ResolvableType.forClass(config.getOutClass());
MediaType contentType = exchange.getResponse().getHeaders().getContentType();
Optional<HttpMessageReader<?>> reader = getHttpMessageReader(codecConfigurer, inElementType, contentType);
Optional<HttpMessageWriter<?>> writer = getHttpMessageWriter(codecConfigurer, outElementType, null);
if (reader.isPresent() && writer.isPresent()) {
ResponseAdapter responseAdapter = new ResponseAdapter(body, getDelegate().getHeaders());
Flux<?> modified = reader.get().read(inElementType, responseAdapter, config.getInHints())
.cast(inElementType.resolve())
.flatMap(originalBody -> Flux.just(config.rewriteFunction.apply(exchange, originalBody)))
.cast(outElementType.resolve());
return getDelegate().writeWith(
writer.get().write((Publisher)modified, outElementType, null, getDelegate(),
config.getOutHints())
);
}
// TODO: error? log?
return getDelegate().writeWith(body);
}
@Override
public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {
return writeWith(Flux.from(body)
.flatMapSequential(p -> p));
}
};
return chain.filter(exchange.mutate().response(responseDecorator).build());
}
@Override
public int getOrder() {
return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;
}
}
Mainly used to modify response
GatewayFilterAdapter
spring-cloud-gateway-core-2.0.0.RC2-sources.jar! /org/springframework/cloud/gateway/handler/FilteringWebHandler.java
private static class GatewayFilterAdapter implements GatewayFilter {
private final GlobalFilter delegate;
public GatewayFilterAdapter(GlobalFilter delegate) {
this.delegate = delegate;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return this.delegate.filter(exchange, chain);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("GatewayFilterAdapter{");
sb.append("delegate=").append(delegate);
sb.append('}');
return sb.toString();
}
}
Convert GlobalFilter to adapter for GatewayFilter
GatewayFilterFactory
spring-cloud-gateway-core-2.0.0.RC2-sources.jar! /org/springframework/cloud/gateway/filter/factory/GatewayFilterFactory.java
@FunctionalInterface
public interface GatewayFilterFactory<C> extends ShortcutConfigurable, Configurable<C> {
String NAME_KEY = "name";
String VALUE_KEY = "value";
// useful for javadsl
default GatewayFilter apply(Consumer<C> consumer) {
C config = newConfig();
consumer.accept(config);
return apply(config);
}
default Class<C> getConfigClass() {
throw new UnsupportedOperationException("getConfigClass() not implemented");
}
@Override
default C newConfig() {
throw new UnsupportedOperationException("newConfig() not implemented");
}
GatewayFilter apply(C config);
default String name() {
//TODO: deal with proxys
return NameUtils.normalizeFilterFactoryName(getClass());
}
@Deprecated
default ServerHttpRequest.Builder mutate(ServerHttpRequest request) {
return request.mutate();
}
}
Spring cloud gateway uses factory mode to generate GatewayFilter. You can see that the apply method is defined here and GatewayFilter is generated according to config. GatewayFilterFactory has several abstract classes, namely, AbstractGatewayFilterFactory, AbstractNameValueGatewayFilterFactory (
Inherited AbstractGatewayFilterFactory
)、AbstractChangeRequestUriGatewayFilterFactory。
AbstractGatewayFilterFactory
spring-cloud-gateway-core-2.0.0.RC2-sources.jar! /org/springframework/cloud/gateway/filter/factory/AbstractGatewayFilterFactory.java
public abstract class AbstractGatewayFilterFactory<C>
extends AbstractConfigurable<C> implements GatewayFilterFactory<C> {
@SuppressWarnings("unchecked")
public AbstractGatewayFilterFactory() {
super((Class<C>) Object.class);
}
public AbstractGatewayFilterFactory(Class<C> configClass) {
super(configClass);
}
public static class NameConfig {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
Its direct implementation classes are as follows:
- HystrixGatewayFilterFactory
- ModifyRequestBodyGatewayFilterFactory
- ModifyResponseBodyGatewayFilterFactory
- PrefixPathGatewayFilterFactory
- PreserveHostHeaderGatewayFilterFactory
- RedirectToGatewayFilterFactory
- RemoveRequestHeaderGatewayFilterFactory
- RemoveResponseHeaderGatewayFilterFactory
- RequestRateLimiterGatewayFilterFactory
- RetryGatewayFilterFactory
- RewritePathGatewayFilterFactory
- SaveSessionGatewayFilterFactory
- SecureHeadersGatewayFilterFactory
- SetPathGatewayFilterFactory
- SetStatusGatewayFilterFactory
- StripPrefixGatewayFilterFactory
AbstractNameValueGatewayFilterFactory
spring-cloud-gateway-core-2.0.0.RC2-sources.jar! /org/springframework/cloud/gateway/filter/factory/AbstractNameValueGatewayFilterFactory.java
public abstract class AbstractNameValueGatewayFilterFactory extends AbstractGatewayFilterFactory<AbstractNameValueGatewayFilterFactory.NameValueConfig> {
public AbstractNameValueGatewayFilterFactory() {
super(NameValueConfig.class);
}
public List<String> shortcutFieldOrder() {
return Arrays.asList(GatewayFilter.NAME_KEY, GatewayFilter.VALUE_KEY);
}
@Validated
public static class NameValueConfig {
@NotEmpty
protected String name;
@NotEmpty
protected String value;
public String getName() {
return name;
}
public NameValueConfig setName(String name) {
this.name = name;
return this;
}
public String getValue() {
return value;
}
public NameValueConfig setValue(String value) {
this.value = value;
return this;
}
@Override
public String toString() {
return new ToStringCreator(this)
.append("name", name)
.append("value", value)
.toString();
}
}
}
Restrict generics to abstract namevaluegatewayfilterfactory. namevalueconfig, whose implementation classes are as follows:
- AddRequestHeaderGatewayFilterFactory
- AddRequestParameterGatewayFilterFactory
- AddResponseHeaderGatewayFilterFactory
- SetRequestHeaderGatewayFilterFactory
- SetResponseHeaderGatewayFilterFactory
AbstractChangeRequestUriGatewayFilterFactory
spring-cloud-gateway-core-2.0.0.RC2-sources.jar! /org/springframework/cloud/gateway/filter/factory/AbstractChangeRequestUriGatewayFilterFactory.java
/**
* This filter changes the request uri by
* {@link #determineRequestUri(ServerWebExchange, T)} logic.
*
* @author Toshiaki Maki
*/
public abstract class AbstractChangeRequestUriGatewayFilterFactory<T>
extends AbstractGatewayFilterFactory<T> {
private final int order;
public AbstractChangeRequestUriGatewayFilterFactory(Class<T> clazz, int order) {
super(clazz);
this.order = order;
}
public AbstractChangeRequestUriGatewayFilterFactory(Class<T> clazz) {
this(clazz, RouteToRequestUrlFilter.ROUTE_TO_URL_FILTER_ORDER + 1);
}
protected abstract Optional<URI> determineRequestUri(ServerWebExchange exchange,
T config);
public GatewayFilter apply(T config) {
return new OrderedGatewayFilter((exchange, chain) -> {
Optional<URI> uri = this.determineRequestUri(exchange, config);
uri.ifPresent(u -> {
Map<String, Object> attributes = exchange.getAttributes();
attributes.put(GATEWAY_REQUEST_URL_ATTR, u);
});
return chain.filter(exchange);
}, this.order);
}
}
Set a new uri through GATEWAY_REQUEST_URL_ATTR, whose direct implementation class is
- RequestHeaderToRequestUriGatewayFilterFactory(
Uri is set by the value of header
)
GatewayFilterFactory.apply
spring-cloud-gateway-core-2.0.0.RC2-sources.jar! /org/springframework/cloud/gateway/route/RouteDefinitionRouteLocator.java
private List<GatewayFilter> loadGatewayFilters(String id, List<FilterDefinition> filterDefinitions) {
List<GatewayFilter> filters = filterDefinitions.stream()
.map(definition -> {
GatewayFilterFactory factory = this.gatewayFilterFactories.get(definition.getName());
if (factory == null) {
throw new IllegalArgumentException("Unable to find GatewayFilterFactory with name " + definition.getName());
}
Map<String, String> args = definition.getArgs();
if (logger.isDebugEnabled()) {
logger.debug("RouteDefinition " + id + " applying filter " + args + " to " + definition.getName());
}
Map<String, Object> properties = factory.shortcutType().normalize(args, factory, this.parser, this.beanFactory);
Object configuration = factory.newConfig();
ConfigurationUtils.bind(configuration, properties,
factory.shortcutFieldPrefix(), definition.getName(), validator);
GatewayFilter gatewayFilter = factory.apply(configuration);
if (this.publisher != null) {
this.publisher.publishEvent(new FilterArgsEvent(this, id, properties));
}
return gatewayFilter;
})
.collect(Collectors.toList());
ArrayList<GatewayFilter> ordered = new ArrayList<>(filters.size());
for (int i = 0; i < filters.size(); i++) {
GatewayFilter gatewayFilter = filters.get(i);
if (gatewayFilter instanceof Ordered) {
ordered.add(gatewayFilter);
}
else {
ordered.add(new OrderedGatewayFilter(gatewayFilter, i + 1));
}
}
return ordered;
}
Here, the GatewayFilter of each route is instantiated by factory.
Summary
The GatewayFilter of spring cloud gateway is mainly produced through GatewayFilterFactory. However, GatewayFilterFactory mainly has three abstract classes:
-
AbstractGatewayFilterFactory
- HystrixGatewayFilterFactory
- ModifyRequestBodyGatewayFilterFactory
- ModifyResponseBodyGatewayFilterFactory
- PrefixPathGatewayFilterFactory
- PreserveHostHeaderGatewayFilterFactory
- RedirectToGatewayFilterFactory
- RemoveRequestHeaderGatewayFilterFactory
- RemoveResponseHeaderGatewayFilterFactory
- RequestRateLimiterGatewayFilterFactory
- RetryGatewayFilterFactory
- RewritePathGatewayFilterFactory
- SaveSessionGatewayFilterFactory
- SecureHeadersGatewayFilterFactory
- SetPathGatewayFilterFactory
- SetStatusGatewayFilterFactory
- StripPrefixGatewayFilterFactory
-
AbstractNameValueGateWayfilFactory (inherited from AbstractGatewayFilterFactory)
- AddRequestHeaderGatewayFilterFactory
- AddRequestParameterGatewayFilterFactory
- AddResponseHeaderGatewayFilterFactory
- SetRequestHeaderGatewayFilterFactory
- SetResponseHeaderGatewayFilterFactory
-
AbstractChangeRequestUriGatewayFilterFactory
- RequestHeaderToRequestUriGatewayFilterFactory
doc
- 112. GatewayFilter Factories
- Talk about spring.cloud.gateway.default-filters
- Talk about SecureHeadersGatewayFilter of spring cloud gateway.
- Talk about spring cloud gateway’s ForwardedHeadersFilter.
- Talk about spring cloud gateway’s XForwardedHeadersFilter
- Talk about RemoveHopByHopHeadersFilter of spring cloud gateway.