本文基於 spring cloud gateway 2.0.1
1、簡介
GatewayFilter 網關過濾器用於攔截並鏈式處理web請求,可以實現橫切的與應用無關的需求,比如:安全、訪問超時的設置等。

從類圖中可以看到,GatewayFilter 有三個實現類:
-
OrderedGatewayFilter 是一個有序的網關過濾器
-
GatewayFilterAdapter 是一個適配器類,是web處理器(FilteringWebHandler)中的內部類
-
ModifyResponseGatewayFilter 是一個內部類,用於修改響應體
本文就分別介紹一下網關過濾器的實現類。
2、有序的網關過濾器 OrderedGatewayFilter
過濾器大多都是有優先級的,因此有序的網關過濾器的使用場景會很多。在實現過濾器接口的同時,有序網關過濾器也實現了 Ordered 接口,構造函數中傳入需要代理的網關過濾器以及優先級就可以構造一個有序的網關過濾器。具體的過濾功能的實現在被代理的過濾器中實現的,因此在此只需要調用代理的過濾器即可。
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;
}
--------------------------------省略-------------------------------
}
3、網關過濾器適配器 GatewayFilterAdapter
在網關過濾器鏈 GatewayFilterChain 中會使用 GatewayFilter 過濾請求,GatewayFilterAdapter的作用就是將全局過濾器 GlobalFilter 適配成 網關過濾器 GatewayFilter。
// 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();
}
}
4、ModifyResponseGatewayFilter
ModifyResponseGatewayFilter 是 ModifyResponseBodyGatewayFilterFactory 的內部類,用於修改響應體的信息
// 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) {
Class inClass = config.getInClass();
Class outClass = config.getOutClass();
String originalResponseContentType = exchange.getAttribute(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR);
HttpHeaders httpHeaders = new HttpHeaders();
//explicitly add it in this way instead of 'httpHeaders.setContentType(originalResponseContentType)'
//this will prevent exception in case of using non-standard media types like "Content-Type: image"
httpHeaders.add(HttpHeaders.CONTENT_TYPE, originalResponseContentType);
ResponseAdapter responseAdapter = new ResponseAdapter(body, httpHeaders);
DefaultClientResponse clientResponse = new DefaultClientResponse(responseAdapter, ExchangeStrategies.withDefaults());
//TODO: flux or mono
Mono modifiedBody = clientResponse.bodyToMono(inClass)
.flatMap(originalBody -> config.rewriteFunction.apply(exchange, originalBody));
BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, outClass);
CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, exchange.getResponse().getHeaders());
return bodyInserter.insert(outputMessage, new BodyInserterContext())
.then(Mono.defer(() -> {
long contentLength1 = getDelegate().getHeaders().getContentLength();
Flux<DataBuffer> messageBody = outputMessage.getBody();
//TODO: if (inputStream instanceof Mono) {
HttpHeaders headers = getDelegate().getHeaders();
if (/*headers.getContentLength() < 0 &&*/ !headers.containsKey(HttpHeaders.TRANSFER_ENCODING)) {
messageBody = messageBody.doOnNext(data -> headers.setContentLength(data.readableByteCount()));
}
// }
//TODO: use isStreamingMediaType?
return getDelegate().writeWith(messageBody);
}));
}
@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;
}
}
