编写自定义谓词和过滤器
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) 来匹配给定的 Route。RequestPredicate 是一个函数式接口,因此可以使用 lambda 表达式来实现。需要实现的方法签名是:
boolean test(ServerRequest request)
示例 RequestPredicate 实现
在这个示例中,我们将展示如何实现一个谓词,用于测试特定的HTTP头部是否包含在HTTP请求中。
Spring WebMvc.fn 中的 RequestPredicate 实现 RequestPredicates 以及 GatewayRequestPredicates 中的实现都采用了 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 方法接收一个 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()).build();
}
}
从现有请求中创建一个新的 ServerRequest。这允许我们使用 header() 方法添加头部。这种实现比 HandlerFilterFunction 更简单,因为我们只处理 ServerRequest。
如何使用自定义前置过滤器实现
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 实例。
过滤实现后的示例
在本示例中,我们将向响应中添加一个带有生成值的标头。
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.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