過濾器的工作模式
所有開源框架實(shí)現(xiàn)過濾器的模式都是大同小異,通過一種類似職責(zé)鏈的方式,傳統(tǒng)的職責(zé)鏈模式中的事件會傳遞直到有一個(gè)處理對象接手,而過濾器和傳統(tǒng)的職責(zé)鏈有點(diǎn)不同,它更像是足球隊(duì)開場握手一樣,所有隊(duì)員一字排開,你要從頭到尾依次和所有球員握過手。
Gateway中的過濾器也是一樣的模型,他們經(jīng)過優(yōu)先級的排列,所有網(wǎng)關(guān)調(diào)用請求從最高優(yōu)先級的過濾器開始,一路走到頭,直到被最后一個(gè)過濾器處理。
過濾器的實(shí)現(xiàn)方式
在Gateway中實(shí)現(xiàn)一個(gè)過濾器非常簡單,只要實(shí)現(xiàn)GatewayFilter接口的默認(rèn)方法就好了
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 隨意發(fā)揮
return chain.filter(exchange);
}
這里面有兩個(gè)關(guān)鍵信息:
- ServerWebExchange 這是Spring封裝的HTTP request-response交互協(xié)議,從中我們可以獲取request和response中的各種請求參數(shù),也可以向其中添加內(nèi)容
- GatewayFilterChain 它是過濾器的調(diào)用鏈,在方法結(jié)束的時(shí)候我們需要將exchange對象傳入調(diào)用鏈中的下一個(gè)對象
過濾器的執(zhí)行階段
不同于Spring Cloud中上一代網(wǎng)關(guān)組件Zuul里對過濾器的Pre和Post的定義,Gateway是通過Filter中的代碼來實(shí)現(xiàn)類似Pre和Post的效果。
Pre和Post是指代當(dāng)前過濾器的執(zhí)行階段,Pre是在下一個(gè)過濾器之前被執(zhí)行,Post是在過濾器執(zhí)行過后再執(zhí)行。我們在Gateway Filter中也可以同時(shí)定義Pre和Post執(zhí)行邏輯。
Pre類型
我們就拿AddResponseHeaderGatewayFilterFactory舉例,它可以向Response中添加Header信息:
@Override
public GatewayFilter apply(NameValueConfig config) {
return (exchange, chain) -> {
exchange.getResponse().getHeaders().add(config.getName(), config.getValue());
return chain.filter(exchange);
};
}
這里的具體執(zhí)行方法是定義在調(diào)用“chain.filter()”方法之前,也就是在轉(zhuǎn)發(fā)到下級調(diào)用鏈路之前執(zhí)行的,因此可以理解為一個(gè)Pre類型的過濾器。
Post類型
我們拿SetStatusGatewayFilterFactory舉例,它在過濾器執(zhí)行完畢之后,將制定的HTTP status返回給調(diào)用方。
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
// 這里是業(yè)務(wù)邏輯
}));
這個(gè)過濾器的主要邏輯在then方法中,then是一個(gè)回調(diào)函數(shù),在下級調(diào)用鏈路都完成以后再執(zhí)行,因此這類過濾器可以看做是Post Filter。
過濾器排座次
在Gateway中我們可以通過實(shí)現(xiàn)org.springframework.core.Ordered接口,來給過濾器指定執(zhí)行順序,比如下面的代碼實(shí)現(xiàn)了Ordered接口方法,將過濾器執(zhí)行順序設(shè)置為0:
@Override
public int getOrder() {
return 0;
}
Pre類型的過濾器來說,數(shù)字越大表示優(yōu)先級越高,也就越早被執(zhí)行。但對于Post類型的過濾器,則是數(shù)字越小越先被執(zhí)行。
注意:過濾器基本都在org.springframework.cloud.gateway.filter.factory包里
Header過濾器
這個(gè)系列有很多組過濾器,AddRequestHeader和AddResponseHeader,分別向Request和Response里加入指定Header。相應(yīng)的RemoveRequestHeader和RemoveResponseHeader分別做移除操作,用法也很簡單:
.filters(f -> f.addResponseHeader("who", "gateway-header"))
上面的例子會向header中添加一個(gè)who的屬性,對應(yīng)的值是gateway-header。
StripPrefix過濾器
這是個(gè)比較常用的過濾器,它的作用是去掉部分URL路徑。比如我們的過濾器配置如下:
.route(r -> r.path("/gateway-test/**")
.filters(f -> f.stripPrefix(1))
.uri("lb://FEIGN-SERVICE-PROVIDER/")
)
假如HTTP請求訪問的是
/gateway-test/sample/update
,如果沒有StripPrefix過濾器,那么轉(zhuǎn)發(fā)到FEIGN-SERVICE-PROVIDER服務(wù)的訪問路徑也是一樣的。當(dāng)我們添加了這個(gè)過濾器之后,Gateway就會根據(jù)“stripPrefix(1)”中的值截取URL中的路徑,比如這里我們設(shè)置的是1,那么就去掉一個(gè)前綴,最終發(fā)送給后臺服務(wù)的路徑變成了“/sample/update”
PrefixPath過濾器
它和StripPrefix的作用是完全相反的,會在請求路徑的前面加入前綴
.route(r -> r.path("/gateway-test/**")
.filters(f -> f.prefixPath("go"))
.uri("lb://FEIGN-SERVICE-PROVIDER/")
)
比如說我們訪問“/gateway-test/sample”的時(shí)候,上面例子中配置的過濾器就會把請求發(fā)送到“/go/gateway-test/sample”。
RedirectTo過濾器
它可以把收到特定狀態(tài)碼的請求重定向到一個(gè)指定網(wǎng)址:
.filters(f -> f.redirect(404, "https://www.imooc.com/"))
上面的例子接收HTTP status code和URL兩個(gè)參數(shù),如果請求結(jié)果是404,則重定向到第二個(gè)參數(shù)指定的頁面,這個(gè)功能也可以做統(tǒng)一異常處理,將Unauthorized或Forbidden請求重定向到登錄頁面。
SaveSession過濾器
我們知道微服務(wù)是無狀態(tài)的會話,所以大多都不依賴session機(jī)制,但是如果你有分布式session的需求,比如說某些功能是基于spring-session和spring-security來實(shí)現(xiàn)的,那么這個(gè)過濾器或許對你有用,它在調(diào)用服務(wù)之前都會強(qiáng)制保存session
.filters(f -> f.saveSession())