编写自定义谓词和过滤器
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