跳到主要内容
版本:5.0.1

编写自定义谓词和过滤器

DeepSeek V3 中英对照 Writing Custom Predicates and Filters

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 实现​

RouterFunctions.Builder 提供了三种添加过滤器的方式:filter、before 和 after。其中 before 和 after 方法是通用 filter 方法的特化形式。

实现 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