編寫自定義謂詞和過濾器
Spring Cloud Gateway Server MVC 使用 Spring WebMvc.fn API(javadoc)作為 API Gateway 功能的基礎。
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 中的 RequestPredicates 和 GatewayRequestPredicates 中的 RequestPredicate 實現都作為 static 方法實現。我們將在此處執行相同的操作。
import org.springframework.web.servlet.function.RequestPredicate;
class SampleRequestPredicates {
public static RequestPredicate headerExists(String header) {
return request -> request.headers().asHttpHeaders().containsKey(header);
}
}
該實現是一個簡單的 lambda,它將 ServerRequest.Headers 物件轉換為更豐富的 HttpHeaders API。這允許謂詞測試命名 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 過濾器實現
before 方法接受一個 Function<ServerRequest, ServerRequest> 作為引數。這允許建立具有更新資料的新 ServerRequest 以從函式返回。
Before 函式可以透過 HandlerFilterFunction.ofRequestProcessor() 適配為 HandlerFilterFunction 例項。 |
Before 過濾器示例實現
在此示例中,我們將向請求新增一個具有生成值的頭。
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 過濾器實現
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 過濾器實現
after 方法接受一個 BiFunction<ServerRequest,ServerResponse,ServerResponse>。這允許訪問 ServerRequest 和 ServerResponse,並且能夠返回一個帶有更新資訊的新 ServerResponse。
After 函式可以透過 HandlerFilterFunction.ofResponseProcessor() 適配為 HandlerFilterFunction 例項。 |
After 過濾器示例實現
在此示例中,我們將向響應新增一個具有生成值的頭。
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 過濾器實現
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()。
如何註冊自定義謂詞和過濾器以進行配置
要在外部配置中使用自定義謂詞和過濾器,您需要建立一個特殊的 Supplier 類並將其註冊為應用程式上下文中的 bean。
註冊自定義謂詞
要註冊自定義謂詞,您需要實現 PredicateSupplier。PredicateDiscoverer 查詢返回 RequestPredicates 的靜態方法以進行註冊。
SampleFilterSupplier.java
import org.springframework.cloud.gateway.server.mvc.predicate.PredicateSupplier;
class SamplePredicateSupplier implements PredicateSupplier {
@Override
public Collection<Method> get() {
return Arrays.asList(SampleRequestPredicates.class.getMethods());
}
}
要註冊 PredicateSupplier 以便在配置檔案中使用,您需要將該類新增為 bean,如以下示例所示
@Configuration
class PredicateConfiguration {
@Bean
public SamplePredicateSupplier samplePredicateSupplier() {
return new SamplePredicateSupplier();
}
}
將類新增到 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;
class SampleFilterSupplier extends SimpleFilterSupplier {
public SampleFilterSupplier() {
super(SampleAfterFilterFunctions.class);
}
}
要註冊 FilterSupplier 以便在配置檔案中使用,您需要將該類新增為 bean,如以下示例所示
@Configuration
class FilterConfiguration {
@Bean
public SampleFilterSupplier sampleFilterSupplier() {
return new SampleFilterSupplier();
}
}
將類新增到 META-INF/spring.factories 的要求已棄用,並將在下一個主要版本中刪除。
org.springframework.cloud.gateway.server.mvc.filter.FilterSupplier=\
com.example.SampleFilterSupplier