Writing Custom Predicates and Filters
Spring Cloud Gateway Server MVC 使用 Spring WebMvc.fn API ( javadoc) 作为 API 网关功能的基础。
Spring Cloud Gateway Server MVC uses the Spring WebMvc.fn API (javadoc) as the basis for the API Gateway functionality.
可以使用这些 API 扩展 Spring Cloud Gateway Server MVC。用户可能通常希望编写 RequestPredicate
和 HandlerFilterFunction
的自定义实现,以及 HandlerFilterFunction
的两个变体,一个用于“before”过滤器,另一个用于“after”过滤器。
Spring Cloud Gateway Server MVC is extensible using these APIs. Users might commonly expect to write custom implementations of RequestPredicate
and HandlerFilterFunction
and two variations of HandlerFilterFunction
, one for "before" filters and another for "after" filters.
Fundamentals
Spring WebMvc.fn API 的一部分的最基本接口是 ServerRequest
( javadoc) 和 ServerResponse ( javadoc)。这些接口可访问 HTTP 请求和响应的所有部分。
The most basic interfaces that are a part of the Spring WebMvc.fn API are ServerRequest
(javadoc) and ServerResponse (javadoc). These provide access to all parts of the HTTP request and response.
Spring WebMvc.fn 文档 declare 中说明: |
The Spring WebMvc.fn docs declare that "`ServerRequest` and |
Implementing a RequestPredicate
Spring WebMvc.fn RouterFunctions.Builder 期望 RequestPredicate
( javadoc) 来匹配给定的 Route 。RequestPredicate
是功能性接口,因此可以用 lambda 函数实现。要实现的方法签名是:
The Spring WebMvc.fn RouterFunctions.Builder expects a RequestPredicate
(javadoc) to match a given Route. RequestPredicate
is a functional interface and can therefor be implemented with lambdas. The method signature to implement is:
boolean test(ServerRequest request)
Example RequestPredicate Implementation
在此示例中,我们将展示一个谓词的实现,以测试特定 HTTP 标头是否属于 HTTP 请求的一部分。
For this example, we will show the implementation of a predicate to test that a particular HTTP headers is part of the HTTP request.
Spring WebMvc.fn RequestPredicates
和 GatewayRequestPredicates 中的 RequestPredicate
实现全部实现为 static
方法。我们将在此处执行相同的操作。
The RequestPredicate
implementations in Spring WebMvc.fn RequestPredicates
and in GatewayRequestPredicates are all implemented as static
methods. We will do the same here.
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
的存在。
The implementation is a simple lambda that transforms the ServerRequest.Headers object to the richer API of HttpHeaders. This allows the predicate to test for the presence of the named header
.
How To Use A Custom RequestPredicate
若要使用新的 headerExists
RequestPredicate
,我们需要将其插入到 RouterFunctions.Builder
上的适当方法中,例如 route()。当然,在下面的示例中,headerExists
方法中的 lambda 函数可以内联编写。
To use our new headerExists
RequestPredicate
, we need to plug it in to an appropriate method on the RouterFunctions.Builder
such as route(). Of course, the lambda in the headerExists
method could be written inline in the example below.
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
的标头时,将匹配上述路由。
The above route will be matched when an HTTP request has a header named X-Green
.
Writing Custom HandlerFilterFunction Implementations
The RouterFunctions.Builder
has three options to add filters: filter, before, and after. The before
and after
methods are specializations of the general filter
method.
Implementing a HandlerFilterFunction
filter
方法采用 HandlerFilterFunction 作为参数。HandlerFilterFunction<T extends ServerResponse, R extends ServerResponse>
是功能性接口,因此可以用 lambda 函数实现。要实现的方法签名是:
The filter
method takes a HandlerFilterFunction as a parameter. HandlerFilterFunction<T extends ServerResponse, R extends ServerResponse>
is a functional interface and can therefor be implemented with lambdas. The method signature to implement is:
R filter(ServerRequest request, HandlerFunction<T> next)
这允许访问 ServerRequest
,并在调用 next.handle(request)
后,才能访问 ServerResponse
。
This allows access to the ServerRequest
and after calling next.handle(request)
access to the ServerResponse
is available.
Example HandlerFilterFunction Implementation
此示例将展示向请求和响应添加标头。
This example will show adding a header to both the request and response.
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
,我们将标头添加到响应中。
First, a new ServerRequest
is created from the existing request. This allows us to add the header using the header()
method. Then we call next.handle()
passing in the modified ServerRequest
. Then using the returned ServerResponse
we add the header to the response.
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
标头。
The above route will add a X-Request-Id
header to the request and a X-Response-Id
header to the response.
Writing Custom Before Filter Implementations
before
方法将 Function<ServerRequest, ServerRequest>
作为参数。这样就可以创建一个新的 ServerRequest
,其中包含要从函数返回的更新数据。
The before
method takes a Function<ServerRequest, ServerRequest>
as a parameter. This allows for creating a new ServerRequest
with updated data to be returned from the function.
在此之前,可通过 HandlerFilterFunction.ofRequestProcessor() 将函数适于 |
Before functions may be adapted to |
Example Before Filter Implementation
在此示例中,我们将向请求中添加一个具有生成值的新标头。
In this example we will add a header with a generated value to the request.
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
。
A new ServerRequest
is created from the existing request. This allows us to add the header using the header()
method. This implementation is simpler than the HandlerFilterFunction
because we only deal with the 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()
方法。
The above route will add a X-Request-Id
header to the request. Note the use of the before()
method, rather than filter()
.
Writing Custom After Filter Implementations
after
方法采用了 BiFunction<ServerRequest,ServerResponse,ServerResponse>
。通过这种方法,既可以访问 ServerRequest
也可以访问 ServerResponse
,并且能够返回包含更新信息的新 ServerResponse
。
The after
method takes a BiFunction<ServerRequest,ServerResponse,ServerResponse>
. This allows access to both the ServerRequest
and the ServerResponse
and the ability to return a new ServerResponse
with updated information.
可以通过 HandlerFilterFunction.ofResponseProcessor() 在 |
After functions may be adapted to |
Example After Filter Implementation
在此示例中,我们将向响应添加一个带有生成值的头信息。
In this example we will add a header with a generated value to the response.
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;
};
}
}
在此示例中,我们只需将头信息添加到响应并返回即可。
In this case we simply add the header to the response and return it.
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()
方法。
The above route will add a X-Response-Id
header to the response. Note the use of the after()
method, rather than filter()
.
How To Register Custom Predicates and Filters for Configuration
要在外部配置中使用自定义谓词和筛选器,需要创建专门的 Supplier
类并将其注册到 META-INF/spring.factories
中。
To use custom Predicates and Filters in external configuration you need to create a special Supplier class and register it in META-INF/spring.factories
.
Registering Custom Predicates
要注册自定义谓词,需要实现 PredicateSupplier
。PredicateDiscoverer
查找返回 RequestPredicates
的静态方法来进行注册。
To register custom predicates you need to implement PredicateSupplier
. The PredicateDiscoverer
looks for static methods that return RequestPredicates
to register.
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
中。
You then need to add the class in META-INF/spring.factories
.
org.springframework.cloud.gateway.server.mvc.predicate.PredicateSupplier=\
com.example.SamplePredicateSupplier
Registering Custom Filters
SimpleFilterSupplier
允许轻松注册自定义筛选器。FilterDiscoverer
查找返回 HandlerFilterFunction
的静态方法来进行注册。如果你需要 SimpleFilterSupplier
更灵活的功能,可以直接实现 FilterSupplier
。
The SimpleFilterSupplier
allows for easily registering custom filters. The FilterDiscoverer
looks for static methods that return HandlerFilterFunction
to register. If you need more flexibility than SimpleFilterSupplier
you can implement FilterSupplier
directly.
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
中。
You then need to add the class in META-INF/spring.factories
.
org.springframework.cloud.gateway.server.mvc.filter.FilterSupplier=\
com.example.SampleFilterSupplier