编写自定义谓词和过滤器
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 实现
在本例中,我们将展示如何实现一个谓词(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);
}
}
实现是一个简单的 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();
}
}
上述路由将在 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.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();
}
}
上述路由将向请求添加一个 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();
}
}
从现有的请求中创建一个新的 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();
}
}
上述路由将会在请求中添加一个 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;
};
}
}
在这种情况下,我们只需将标头添加到响应中并返回它。
如何使用自定义的 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();
}
}
上述路由将为响应添加一个 X-Response-Id
头信息。注意这里使用的是 after()
方法,而不是 filter()
。
如何为配置注册自定义谓词和过滤器
要在外部配置中使用自定义的 Predicates 和 Filters,你需要创建一个特殊的 Supplier
类,并将其注册到 META-INF/spring.factories
中。
注册自定义谓词
要注册自定义谓词,你需要实现 PredicateSupplier
。PredicateDiscoverer
会查找返回 RequestPredicates
的静态方法来进行注册。
SampleFilterSupplier.java
// 示例过滤器供应商类
public class SampleFilterSupplier {
// 这是一个示例过滤器供应商类,用于提供过滤器实例
// 你可以根据需要自定义过滤器的逻辑
}
在这个示例中,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());
}
}
然后你需要在 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;
@Configuration
class SampleFilterSupplier extends SimpleFilterSupplier {
public SampleFilterSupplier() {
super(SampleAfterFilterFunctions.class);
}
}
然后你需要在 META-INF/spring.factories
中添加类。
org.springframework.cloud.gateway.server.mvc.filter.FilterSupplier=\
com.example.SampleFilterSupplier