前提
GatewayFilter
的作用域是指定的路由配置,路由配置选项里面需要通过filters指定想要使用的GatewayFilter
列表。我们可以通过自定义GatewayFilter
,做额外的扩展,实现一些内建GatewayFilter
不存在的功能,并且应用到我们的路由配置中。
如何自定义GatewayFilter
需要定制GatewayFilter
,则需要实现org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory
接口,GatewayFilterFactory
的定义如下:
@FunctionalInterface public interface GatewayFilterFactory<C> extends ShortcutConfigurable, Configurable<C> {
String NAME_KEY = "name";
String VALUE_KEY = "value";
default GatewayFilter apply(Consumer<C> consumer) { C config = newConfig(); consumer.accept(config); return apply(config); }
default Class<C> getConfigClass() { throw new UnsupportedOperationException("getConfigClass() not implemented"); }
@Override default C newConfig() { throw new UnsupportedOperationException("newConfig() not implemented"); }
GatewayFilter apply(C config);
default String name() { return NameUtils.normalizeFilterFactoryName(getClass()); }
@Deprecated default ServerHttpRequest.Builder mutate(ServerHttpRequest request) { return request.mutate(); } }
public interface ShortcutConfigurable {
default ShortcutType shortcutType() { return ShortcutType.DEFAULT; }
default List<String> shortcutFieldOrder() { return Collections.emptyList(); }
default String shortcutFieldPrefix() { return ""; } }
public interface Configurable<C> {
Class<C> getConfigClass();
C newConfig(); }
|
看起来挺复杂的,但是实际上很多都是接口的默认方法,实际上要实现的方法很少。
另一种方式是继承org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory
,看抽象类AbstractGatewayFilterFactory
的定义:
public abstract class AbstractGatewayFilterFactory<C> extends AbstractConfigurable<C> implements GatewayFilterFactory<C> {
@SuppressWarnings("unchecked") public AbstractGatewayFilterFactory() { super((Class<C>) Object.class); }
public AbstractGatewayFilterFactory(Class<C> configClass) { super(configClass); }
public static class NameConfig {
private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; } } }
|
泛型参数C是配置类,从现有的AbstractGatewayFilterFactory
或者GatewayFilterFactory
的子类实现来看,配置类一般定义为公有静态内部类。
- 实现
GatewayFilterFactory
接口或者继承AbstractGatewayFilterFactory
。
- 对应的子类注册到Spring的容器。
- 路由配置中的filters属性添加对应
GatewayFilter
配置,注意一下,过滤器名称由GatewayFilterFactory#name()
决定。
实践
下面实现GatewayFilterFactory
接口和继承AbstractGatewayFilterFactory
抽象类两种方式都做了尝试。
实现GatewayFilterFactory接口
实现GatewayFilterFactory
接口的时候,参考了SetRequestHeaderGatewayFilterFactory
,为每个请求添加自定义的请求头。
@Component public class CustomAddRequestHeaderGatewayFilterFactory implements GatewayFilterFactory<CustomAddRequestHeaderGatewayFilterFactory.CustomAddRequestHeaderConfig> {
private final Class<CustomAddRequestHeaderConfig> configClass = CustomAddRequestHeaderConfig.class;
@Override public List<String> shortcutFieldOrder() { return new ArrayList<>(Arrays.asList("headerName", "headerValue")); }
@Override public GatewayFilter apply(CustomAddRequestHeaderConfig config) { return ((exchange, chain) -> { ServerHttpRequest request = exchange.getRequest().mutate().headers(httpHeaders -> { httpHeaders.set(config.getHeaderName(), config.getHeaderValue()); }).build(); return chain.filter(exchange.mutate().request(request).build()); }); }
@Override public Class<CustomAddRequestHeaderConfig> getConfigClass() { return configClass; }
@Override public CustomAddRequestHeaderConfig newConfig() { return BeanUtils.instantiateClass(this.configClass); }
public static class CustomAddRequestHeaderConfig {
private String headerName; private String headerValue;
public String getHeaderName() { return headerName; }
public void setHeaderName(String headerName) { this.headerName = headerName; }
public String getHeaderValue() { return headerValue; }
public void setHeaderValue(String headerValue) { this.headerValue = headerValue; } } }
|
可以看到,其实最核心的功能操作是需要实现GatewayFilter apply(C config)
方法,编写自定义的功能。注意这段Lambda表达式的逻辑:
return ((exchange, chain) -> { ServerHttpRequest request = exchange.getRequest().mutate().headers(httpHeaders -> { httpHeaders.set(config.getHeaderName(), config.getHeaderValue()); }).build(); return chain.filter(exchange.mutate().request(request).build()); });
|
其实,它可以简单理解为GatewayFilter
接口的匿名实现。
application.yaml配置如下:
spring: cloud: gateway: routes: - id: custom_add_request_header_route uri: http://localhost:9091 predicates: - Host=localhost:9090 filters: - CustomAddRequestHeader=customHeaderName,customHeaderValue
|
为了配合测试,下游服务添加一个端点:
@GetMapping(value = "/customAddRequestHeader") public ResponseEntity<String> customAddRequestHeader(@RequestHeader(name = "customHeaderName") String value) { return ResponseEntity.ok(value); }
|
curl localhost:9090/order/customAddRequestHeader
// 响应 customHeaderValue
|
继承AbstractGatewayFilterFactory抽象类
继承AbstractGatewayFilterFactory
的方式其实差不多,我们尝试做一个相对复杂的改造:对每一个请求成功(状态码为200)的响应,添加一个自定义的cookie和一个响应头。
@Component public class CustomResponseGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomResponseGatewayFilterFactory.Config> {
public CustomResponseGatewayFilterFactory() { super(Config.class); }
@Override public GatewayFilter apply(Config config) { return ((exchange, chain) -> { ServerHttpResponse response = exchange.getResponse(); if (HttpStatus.OK.equals(response.getStatusCode())) { for (Map.Entry<String, String> entry : toMap(config.getCookie()).entrySet()) { response.addCookie(ResponseCookie.from(entry.getKey(), entry.getValue()).build()); } for (Map.Entry<String, String> entry : toMap(config.getHeader()).entrySet()) { response.getHeaders().add(entry.getKey(), entry.getValue()); } return chain.filter(exchange.mutate().response(response).build()); } else { return chain.filter(exchange); } }); }
@Override public List<String> shortcutFieldOrder() { return Collections.singletonList(NAME_KEY); }
private static Map<String, String> toMap(String value) { String[] split = value.split("="); Map<String, String> map = new HashMap<>(8); map.put(split[0], split[1]); return map; }
public static class Config {
private String cookie; private String header;
public String getCookie() { return cookie; }
public void setCookie(String cookie) { this.cookie = cookie; }
public String getHeader() { return header; }
public void setHeader(String header) { this.header = header; } } }
|
application.yaml配置文件如下:
spring: cloud: gateway: routes: - id: custom_response_route uri: http://localhost:9091 predicates: - Host=localhost:9090 filters: - name: CustomResponse args: cookie: customCookieName=customCookieValue header: customHeaderName=customHeaderValue
|
这里注意,filters集合下的name属性是必须的,指向AbstractGatewayFilterFactory
实现类的name()
方法,args属性是用于指定装配到Config
类的属性。
curl localhost:9090/order/remote
// 响应头如下 customHeaderName: customHeaderValue Content-Type: text/plain;charset=UTF-8 Content-Length: 6 Date: Sun, 05 May 2019 11:06:35 GMT set-cookie: customCookieName=customCookieValue
|
小结
自定义GatewayFilter
允许我们很灵活地扩展过滤器,并且对于请求或者响应添加自定义的一些属性或者判断逻辑。GatewayFilter
不是全局生效的特性,使得我们在编写路由配置的时候可以灵活组合多个已经编写好的GatewayFilter
实例的功能。
(c-1-d e-a-20190505)