撰寫自訂謂詞與篩選器
Spring Cloud Gateway Server MVC 使用 Spring WebMvc.fn API (javadoc) 作為 API 閘道功能的基礎。
Spring Cloud Gateway Server MVC 可使用這些 API 進行擴充。使用者通常可能期望撰寫 RequestPredicate
和 HandlerFilterFunction
的自訂實作,以及 HandlerFilterFunction
的兩種變體,一種用於「前置」篩選器,另一種用於「後置」篩選器。
基礎知識
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) 以比對給定的 路由。RequestPredicate
是一個函數介面,因此可以使用 lambda 實作。要實作的方法簽章為
boolean test(ServerRequest request)
RequestPredicate 實作範例
在此範例中,我們將展示謂詞的實作,以測試特定 HTTP 標頭是否為 HTTP 請求的一部分。
Spring WebMvc.fn 中的 RequestPredicate
實作 RequestPredicates
和 GatewayRequestPredicates 皆實作為 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 物件轉換為更豐富的 API HttpHeaders。這允許謂詞測試是否存在指定的 header
。
如何使用自訂 RequestPredicate
若要使用我們新的 headerExists
RequestPredicate
,我們需要將其插入 RouterFunctions.Builder
上的適當方法,例如 route()。當然,headerExists
方法中的 lambda 可以直接寫入以下範例中。
import static SampleRequestPredicates.headerExists;
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("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());
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.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("https://example.org"))
.filter(instrument("X-Request-Id", "X-Response-Id"))
.build();
}
}
上述路由會將 X-Request-Id
標頭新增至請求,並將 X-Response-Id
標頭新增至回應。
撰寫自訂前置篩選器實作
before
方法採用 Function<ServerRequest, ServerRequest>
作為參數。這允許建立新的 ServerRequest
,其中包含要從函數傳回的更新資料。
前置函數可以透過 HandlerFilterFunction.ofRequestProcessor() 適配至 HandlerFilterFunction 實例。 |
前置篩選器實作範例
在此範例中,我們將新增一個具有產生值的標頭至請求。
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());;
}
}
從現有請求建立新的 ServerRequest
。這讓我們可以使用 header()
方法新增標頭。此實作比 HandlerFilterFunction
更簡單,因為我們僅處理 ServerRequest
。
如何使用自訂前置篩選器實作
import static SampleBeforeFilterFunctions.instrument;
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("https://example.org"))
.before(instrument("X-Request-Id"))
.build();
}
}
上述路由會將 X-Request-Id
標頭新增至請求。請注意使用 before()
方法,而非 filter()
。
撰寫自訂後置篩選器實作
after
方法採用 BiFunction<ServerRequest,ServerResponse,ServerResponse>
。這允許存取 ServerRequest
和 ServerResponse
,並能夠傳回具有更新資訊的新 ServerResponse
。
後置函數可以透過 HandlerFilterFunction.ofResponseProcessor() 適配至 HandlerFilterFunction 實例。 |
後置篩選器實作範例
在此範例中,我們將新增一個具有產生值的標頭至回應。
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;
};
}
}
在此案例中,我們僅將標頭新增至回應並傳回。
如何使用自訂後置篩選器實作
import static SampleAfterFilterFunctions.instrument;
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("https://example.org"))
.after(instrument("X-Response-Id"))
.build();
}
}
上述路由會將 X-Response-Id
標頭新增至回應。請注意使用 after()
方法,而非 filter()
。
如何註冊用於組態的自訂謂詞和篩選器
若要在外部組態中使用自訂謂詞和篩選器,您需要建立特殊的 Supplier 類別,並在 META-INF/spring.factories
中註冊它。
註冊自訂謂詞
若要註冊自訂謂詞,您需要實作 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
註冊自訂篩選器
SimpleFilterSupplier
允許輕鬆註冊自訂篩選器。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