編寫自定義 Predicate 和 Filter
Spring Cloud Gateway Server MVC 使用 Spring WebMvc.fn API (javadoc) 作為 API 閘道器功能的基礎。
Spring Cloud Gateway Server MVC 使用這些 API 可進行擴充套件。使用者通常會編寫 RequestPredicate
和 HandlerFilterFunction
的自定義實現,以及 HandlerFilterFunction
的兩種變體,一種用於“before”過濾器,另一種用於“after”過濾器。
基礎
Spring WebMvc.fn API 中最基本的介面是 ServerRequest
(javadoc) 和 ServerResponse (javadoc)。它們提供了對 HTTP 請求和響應所有部分的訪問。
Spring WebMvc.fn 文件 宣告 “`ServerRequest` 和 `ServerResponse` 是不可變介面。在某些情況下,Spring Cloud Gateway Server MVC 必須提供備用實現,以便某些內容可以變為可變,以滿足 API 閘道器的代理需求。 |
實現 RequestPredicate
Spring WebMvc.fn RouterFunctions.Builder 期望一個 RequestPredicate
(javadoc) 來匹配給定的 Route。RequestPredicate
是一個函式式介面,因此可以使用 lambda 實現。需要實現的的方法簽名是
boolean test(ServerRequest request)
RequestPredicate 實現示例
本示例將展示一個 predicate 的實現,用於測試特定的 HTTP 頭部是否屬於 HTTP 請求的一部分。
Spring WebMvc.fn RequestPredicates
和 GatewayRequestPredicates 中的 RequestPredicate
實現都作為 static
方法實現。我們在此也這樣做。
import org.springframework.web.reactive.function.server.RequestPredicate;
class SampleRequestPredicates {
public static RequestPredicate headerExists(String header) {
return request -> request.headers().asHttpHeaders().containsKey(header);
}
}
該實現是一個簡單的 lambda,將 ServerRequest.Headers 物件轉換為更豐富的 HttpHeaders API。這允許 predicate 測試指定 header
是否存在。
如何使用自定義 RequestPredicate
要使用我們新的 headerExists
RequestPredicate
,我們需要將其插入到 RouterFunctions.Builder
上的適當方法中,例如 route()。當然,headerExists
方法中的 lambda 可以在下面的示例中內聯編寫。
import static SampleRequestPredicates.headerExists;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
@Configuration
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> headerExistsRoute() {
return route("header_exists_route")
.route(headerExists("X-Green"), http())
.before(uri("https://example.org"))
.build();
}
}
當 HTTP 請求包含名為 X-Green
的頭部時,上述路由將被匹配。
編寫自定義 HandlerFilterFunction 實現
實現 HandlerFilterFunction
filter
方法將 HandlerFilterFunction 作為引數。HandlerFilterFunction<T extends ServerResponse, R extends ServerResponse>
是一個函式式介面,因此可以使用 lambda 實現。需要實現的方法簽名是
R filter(ServerRequest request, HandlerFunction<T> next)
這允許訪問 ServerRequest
,並且在呼叫 next.handle(request)
後,可以訪問 ServerResponse
。
HandlerFilterFunction 實現示例
本示例將展示如何向請求和響應都新增頭部。
import org.springframework.web.servlet.function.HandlerFilterFunction;
import org.springframework.web.servlet.function.ServerRequest;
import org.springframework.web.servlet.function.ServerResponse;
class SampleHandlerFilterFunctions {
public static HandlerFilterFunction<ServerResponse, ServerResponse> instrument(String requestHeader, String responseHeader) {
return (request, next) -> {
ServerRequest modified = ServerRequest.from(request).header(requestHeader, generateId()).build();
ServerResponse response = next.handle(modified);
response.headers().add(responseHeader, generateId());
return response;
};
}
}
首先,從現有請求建立一個新的 ServerRequest
。這允許我們使用 header()
方法新增頭部。然後我們呼叫 next.handle()
並傳入修改後的 ServerRequest
。然後使用返回的 ServerResponse
,我們將頭部新增到響應中。
如何使用自定義 HandlerFilterFunction 實現
import static SampleHandlerFilterFunctions.instrument;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
@Configuration
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> instrumentRoute() {
return route("instrument_route")
.GET("/**", http())
.filter(instrument("X-Request-Id", "X-Response-Id"))
.before(uri("https://example.org"))
.build();
}
}
上述路由將向請求新增 X-Request-Id
頭部,並向響應新增 X-Response-Id
頭部。
編寫自定義 Before Filter 實現
before
方法將 Function<ServerRequest, ServerRequest>
作為引數。這允許建立帶有更新資料的新 ServerRequest
,並從函式返回。
Before 函式可以透過 HandlerFilterFunction.ofRequestProcessor() 適應於 HandlerFilterFunction 例項。 |
Before Filter 實現示例
本示例中,我們將向請求新增一個帶有生成值的頭部。
import java.util.function.Function;
import org.springframework.web.servlet.function.ServerRequest;
class SampleBeforeFilterFunctions {
public static Function<ServerRequest, ServerRequest> instrument(String header) {
return request -> ServerRequest.from(request).header(header, generateId()).build();
}
}
從現有請求建立一個新的 ServerRequest
。這允許我們使用 header()
方法新增頭部。這個實現比 HandlerFilterFunction
更簡單,因為我們只處理 ServerRequest
。
如何使用自定義 Before Filter 實現
import static SampleBeforeFilterFunctions.instrument;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
@Configuration
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> instrumentRoute() {
return route("instrument_route").GET("/**", http())
.before(uri("https://example.org"))
.before(instrument("X-Request-Id"))
.build();
}
}
上述路由將向請求新增 X-Request-Id
頭部。注意使用了 before()
方法,而不是 filter()
。
編寫自定義 After Filter 實現
after
方法接受一個 BiFunction<ServerRequest,ServerResponse,ServerResponse>
。這允許訪問 ServerRequest
和 ServerResponse
,並能夠返回帶有更新資訊的新 ServerResponse
。
After 函式可以透過 HandlerFilterFunction.ofResponseProcessor() 適應於 HandlerFilterFunction 例項。 |
After Filter 實現示例
本示例中,我們將向響應新增一個帶有生成值的頭部。
import java.util.function.BiFunction;
import org.springframework.web.servlet.function.ServerRequest;
import org.springframework.web.servlet.function.ServerResponse;
class SampleAfterFilterFunctions {
public static BiFunction<ServerRequest, ServerResponse, ServerResponse> instrument(String header) {
return (request, response) -> {
response.headers().add(header, generateId());
return response;
};
}
}
在這種情況下,我們只需將頭部新增到響應並返回。
如何使用自定義 After Filter 實現
import static SampleAfterFilterFunctions.instrument;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
@Configuration
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> instrumentRoute() {
return route("instrument_route")
.GET("/**", http())
.before(uri("https://example.org"))
.after(instrument("X-Response-Id"))
.build();
}
}
上述路由將向響應新增 X-Response-Id
頭部。注意使用了 after()
方法,而不是 filter()
。
如何註冊自定義 Predicate 和 Filter 以用於配置
要在外部配置中使用自定義 Predicate 和 Filter,您需要建立一個特殊的 Supplier 類並將其註冊在 META-INF/spring.factories
中。
註冊自定義 Predicate
要註冊自定義 predicate,您需要實現 PredicateSupplier
。PredicateDiscoverer
查詢返回 RequestPredicates
的靜態方法進行註冊。
SampleFilterSupplier.java
import org.springframework.cloud.gateway.server.mvc.predicate.PredicateSupplier;
@Configuration
class SamplePredicateSupplier implements PredicateSupplier {
@Override
public Collection<Method> get() {
return Arrays.asList(SampleRequestPredicates.class.getMethods());
}
}
然後您需要在 META-INF/spring.factories
中新增該類。
org.springframework.cloud.gateway.server.mvc.predicate.PredicateSupplier=\
com.example.SamplePredicateSupplier
註冊自定義 Filter
SimpleFilterSupplier
允許輕鬆註冊自定義 filter。FilterDiscoverer
查詢返回 HandlerFilterFunction
的靜態方法進行註冊。如果您需要比 SimpleFilterSupplier
更大的靈活性,可以直接實現 FilterSupplier
。
import org.springframework.cloud.gateway.server.mvc.filter.SimpleFilterSupplier;
@Configuration
class SampleFilterSupplier extends SimpleFilterSupplier {
public SampleFilterSupplier() {
super(SampleAfterFilterFunctions.class);
}
}
然後您需要在 META-INF/spring.factories
中新增該類。
org.springframework.cloud.gateway.server.mvc.filter.FilterSupplier=\
com.example.SampleFilterSupplier