跳到主要内容

编写自定义谓词和过滤器

DeepSeek V3 中英对照 Writing Custom Predicates and Filters

Spring Cloud Gateway Server MVC 使用 Spring WebMvc.fn API (javadoc) 作为 API 网关功能的基础。

Spring Cloud Gateway Server MVC 可以使用这些 API 进行扩展。用户通常可能会编写 RequestPredicateHandlerFilterFunction 的自定义实现,以及两种变体的 HandlerFilterFunction,一种用于“前置”过滤器,另一种用于“后置”过滤器。

基础

Spring WebMvc.fn API 中最基本的接口是 ServerRequest (javadoc) 和 ServerResponse (javadoc)。这些接口提供了对 HTTP 请求和响应的所有部分的访问。

备注

Spring WebMvc.fn 文档 声明 称:“ServerRequestServerResponse 是不可变的接口。在某些情况下,Spring Cloud Gateway Server MVC 必须提供替代实现,以便某些内容可以变为可变的,以满足 API 网关的代理需求。”

实现一个 RequestPredicate

Spring WebMvc.fn 的 RouterFunctions.Builder 期望一个 RequestPredicate (javadoc) 来匹配给定的 RouteRequestPredicate 是一个函数式接口,因此可以使用 lambda 表达式来实现。需要实现的方法签名是:

boolean test(ServerRequest request)
none

示例 RequestPredicate 实现

在本例中,我们将展示如何实现一个谓词(predicate),用于测试特定的 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);
}
}
java

实现是一个简单的 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.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();
}
}
java

上述路由将在 HTTP 请求中包含名为 X-Green 的头部时匹配。

编写自定义的 HandlerFilterFunction 实现

RouterFunctions.Builder 提供了三种添加过滤器的方式:filterbeforeafter。其中,beforeafter 方法是通用 filter 方法的特化版本。

实现一个 HandlerFilterFunction

filter 方法接受一个 HandlerFilterFunction 作为参数。HandlerFilterFunction<T extends ServerResponse, R extends ServerResponse> 是一个函数式接口,因此可以使用 lambda 表达式来实现。需要实现的方法签名是:

R filter(ServerRequest request, HandlerFunction<T> next)
none

这允许访问 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;
};
}
}
java

首先,从现有的请求中创建一个新的 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();
}
}
java

上述路由将向请求添加一个 X-Request-Id 标头,并向响应添加一个 X-Response-Id 标头。

编写自定义前置过滤器实现

before 方法接收一个 Function<ServerRequest, ServerRequest> 作为参数。这使得可以创建一个带有更新数据的新的 ServerRequest,并从该函数中返回。

备注

在将函数适配为 HandlerFilterFunction 实例之前,可以通过 HandlerFilterFunction.ofRequestProcessor() 来实现。

过滤器实现前的示例

在这个示例中,我们将向请求中添加一个带有生成值的标头。

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();
}
}
java

从现有的请求中创建一个新的 ServerRequest。这允许我们使用 header() 方法添加头部信息。这个实现比 HandlerFilterFunction 更简单,因为我们只处理 ServerRequest

如何使用自定义的 Before Filter 实现

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();
}
}
java

上述路由将会在请求中添加一个 X-Request-Id 头。请注意这里使用的是 before() 方法,而不是 filter()

编写自定义 After Filter 实现

after 方法接收一个 BiFunction<ServerRequest, ServerResponse, ServerResponse> 类型的参数。这使得可以同时访问 ServerRequestServerResponse,并且能够返回一个包含更新信息的新的 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;
};
}
}
java

在这种情况下,我们只需将标头添加到响应中并返回它。

如何使用自定义的 After Filter 实现

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();
}
}
java

上述路由将为响应添加一个 X-Response-Id 头信息。注意这里使用的是 after() 方法,而不是 filter()

如何为配置注册自定义谓词和过滤器

要在外部配置中使用自定义的 Predicates 和 Filters,你需要创建一个特殊的 Supplier 类,并将其注册到 META-INF/spring.factories 中。

注册自定义谓词

要注册自定义谓词,你需要实现 PredicateSupplierPredicateDiscoverer 会查找返回 RequestPredicates 的静态方法来进行注册。

SampleFilterSupplier.java
java
// 示例过滤器供应商类
public class SampleFilterSupplier {
// 这是一个示例过滤器供应商类,用于提供过滤器实例
// 你可以根据需要自定义过滤器的逻辑
}
java

在这个示例中,SampleFilterSupplier.java 是一个 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());
}

}
java

然后你需要在 META-INF/spring.factories 中添加这个类。

org.springframework.cloud.gateway.server.mvc.predicate.PredicateSupplier=\
com.example.SamplePredicateSupplier
none

注册自定义过滤器

SimpleFilterSupplier 允许轻松注册自定义过滤器。FilterDiscoverer 会查找返回 HandlerFilterFunction 的静态方法进行注册。如果你需要比 SimpleFilterSupplier 更多的灵活性,可以直接实现 FilterSupplier

import org.springframework.cloud.gateway.server.mvc.filter.SimpleFilterSupplier;

@Configuration
class SampleFilterSupplier extends SimpleFilterSupplier {

public SampleFilterSupplier() {
super(SampleAfterFilterFunctions.class);
}
}
java

然后你需要在 META-INF/spring.factories 中添加类。

org.springframework.cloud.gateway.server.mvc.filter.FilterSupplier=\
com.example.SampleFilterSupplier
none