Writing Custom Predicates and Filters
Spring Cloud Gateway Server MVC 使用 Spring WebMvc.fn API ( javadoc) 作为 API 网关功能的基础。
可以使用这些 API 扩展 Spring Cloud Gateway Server MVC。用户可能通常希望编写 RequestPredicate
和 HandlerFilterFunction
的自定义实现,以及 HandlerFilterFunction
的两个变体,一个用于“before”过滤器,另一个用于“after”过滤器。
Fundamentals
Spring WebMvc.fn API 的一部分的最基本接口是 ServerRequest
( javadoc) 和 ServerResponse ( javadoc)。这些接口可访问 HTTP 请求和响应的所有部分。
Spring WebMvc.fn 文档 declare 中说明: |
Implementing a RequestPredicate
Spring WebMvc.fn RouterFunctions.Builder 期望 RequestPredicate
( javadoc) 来匹配给定的 Route 。RequestPredicate
是功能性接口,因此可以用 lambda 函数实现。要实现的方法签名是:
boolean test(ServerRequest request)
Example RequestPredicate Implementation
在此示例中,我们将展示一个谓词的实现,以测试特定 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。这允许谓词测试已命名的 header
的存在。
How To Use A Custom 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
的标头时,将匹配上述路由。
Writing Custom HandlerFilterFunction Implementations
Implementing a HandlerFilterFunction
filter
方法采用 HandlerFilterFunction 作为参数。HandlerFilterFunction<T extends ServerResponse, R extends ServerResponse>
是功能性接口,因此可以用 lambda 函数实现。要实现的方法签名是:
R filter(ServerRequest request, HandlerFunction<T> next)
这允许访问 ServerRequest
,并在调用 next.handle(request)
后,才能访问 ServerResponse
。
Example HandlerFilterFunction Implementation
此示例将展示向请求和响应添加标头。
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());
ServerResponse response = next.handle(modified);
response.headers().add(responseHeader, generateId());
return response;
};
}
}
首先,从现有请求中创建一个新的 ServerRequest
。这允许我们使用 header()
方法添加标头。然后,我们调用 next.handle()
,传入修改后的 ServerRequest
。然后,使用返回的 ServerResponse
,我们将标头添加到响应中。
How To Use Custom HandlerFilterFunction Implementations
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
标头。
Writing Custom Before Filter Implementations
before
方法将 Function<ServerRequest, ServerRequest>
作为参数。这样就可以创建一个新的 ServerRequest
,其中包含要从函数返回的更新数据。
在此之前,可通过 HandlerFilterFunction.ofRequestProcessor() 将函数适于 |
Example Before Filter Implementation
在此示例中,我们将向请求中添加一个具有生成值的新标头。
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());;
}
}
从现有请求中创建一个新的 ServerRequest
。这允许我们使用 header()
方法添加标头。此实现比 HandlerFilterFunction
更简单,因为我们只处理 ServerRequest
。
How To Use Custom Before Filter Implementations
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()
方法。
Writing Custom After Filter Implementations
after
方法采用了 BiFunction<ServerRequest,ServerResponse,ServerResponse>
。通过这种方法,既可以访问 ServerRequest
也可以访问 ServerResponse
,并且能够返回包含更新信息的新 ServerResponse
。
可以通过 HandlerFilterFunction.ofResponseProcessor() 在 |
Example After Filter Implementation
在此示例中,我们将向响应添加一个带有生成值的头信息。
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;
};
}
}
在此示例中,我们只需将头信息添加到响应并返回即可。
How To Use Custom After Filter Implementations
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()
方法。
How To Register Custom Predicates and Filters for Configuration
要在外部配置中使用自定义谓词和筛选器,需要创建专门的 Supplier
类并将其注册到 META-INF/spring.factories
中。
Registering Custom Predicates
要注册自定义谓词,需要实现 PredicateSupplier
。PredicateDiscoverer
查找返回 RequestPredicates
的静态方法来进行注册。
SampleFilterSupplier.java
package com.example;
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
Registering Custom Filters
SimpleFilterSupplier
允许轻松注册自定义筛选器。FilterDiscoverer
查找返回 HandlerFilterFunction
的静态方法来进行注册。如果你需要 SimpleFilterSupplier
更灵活的功能,可以直接实现 FilterSupplier
。
package com.example;
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