编写自定义 Predicate 和 Filter
Spring Cloud Gateway Server MVC 使用 Spring WebMvc.fn API (javadoc) 作为 API 网关功能的基础。
Spring Cloud Gateway Server MVC 使用这些 API 可进行扩展。用户通常会编写 RequestPredicate
和 HandlerFilterFunction
的自定义实现,以及 HandlerFilterFunction
的两种变体,一种用于“before”过滤器,另一种用于“after”过滤器。
基础
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 RequestPredicates
和 GatewayRequestPredicates 中的 RequestPredicate
实现都作为 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。这允许 predicate 测试指定 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 Filter 实现
before
方法将 Function<ServerRequest, ServerRequest>
作为参数。这允许创建带有更新数据的新 ServerRequest
,并从函数返回。
Before 函数可以通过 HandlerFilterFunction.ofRequestProcessor() 适应于 HandlerFilterFunction 实例。 |
Before Filter 实现示例
本示例中,我们将向请求添加一个带有生成值的头部。
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.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 实例。 |
After Filter 实现示例
本示例中,我们将向响应添加一个带有生成值的头部。
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.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()
。
如何注册自定义 Predicate 和 Filter 以用于配置
要在外部配置中使用自定义 Predicate 和 Filter,您需要创建一个特殊的 Supplier 类并将其注册在 META-INF/spring.factories
中。
注册自定义 Predicate
要注册自定义 predicate,您需要实现 PredicateSupplier
。PredicateDiscoverer
查找返回 RequestPredicates
的静态方法进行注册。
SampleFilterSupplier.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
注册自定义 Filter
SimpleFilterSupplier
允许轻松注册自定义 filter。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