网关过滤器工厂(Gateway Filter Factory)允许以某种方式修改传入的HTTP请求或返回的HTTP响应。其作用域是某些特定路由。
一、AddRequestHeader过滤工厂
该过滤器工厂会对匹配上的请求添加指定的请求头Header。
1.1 配置式路由方式
spring:
cloud:
gateway:
routes:
- id: my_route
uri: http://localhost:8080
predicates:
- Path=/**
filters:
- AddRequestHeader=X-Request-Color, blue
- AddRequestHeader=X-Request-Color, red
- AddRequestHeader=X-Request-Color, yellow
- AddRequestHeader=X-Request-Color, green
1.2 API式路由方式
@Configuration
public class GatewayConfig {
/**
* AddRequestHeader网关过滤工厂 —— API式
*
* @param builder
* @return
*/
@Bean
public RouteLocator filterAddRequestHeaderRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("filterAddRequestHeader_route", predicateSpec -> predicateSpec.path("/**")
.filters(filterSpec -> filterSpec.addRequestHeader("X-Request-Color", "pink")
.addRequestHeader("X-Request-Color", "black")
.addRequestHeader("X-Request-Color", "white"))
.uri("http://localhost:8080"))
.build();
}
}
1.3 测试
@RestController
@RequestMapping("/info")
public class ShowInfoController {
@GetMapping("/header")
public String headerHandle(HttpServletRequest request) {
Enumeration<String> headers = request.getHeaders("X-Request-Color");
StringBuilder stringBuilder = new StringBuilder();
// 如果headers含有元素,则取出所有值
while (headers.hasMoreElements()) {
stringBuilder.append(headers.nextElement() + " ");
}
return "X-Request-Color: " + stringBuilder.toString();
}
}
二、AddRequestHeadersIfNotPresent过滤工厂
该过滤器工厂会对匹配上的请求添加指定的 header,前提是该 header 在当前请求中尚未出现过。请求中允许出现多个同名的请求头。即同一名称的请求头可以被多次添加到请求中。但有些场景下为了保证请求中对于某请求头只有一个值,就可以使用该过滤工厂。
2.1 配置式路由方式
如果配置了多个AddRequestHeadersIfNotPresent属性,那么只生效第一个,其余的不会生效
spring:
cloud:
gateway:
routes:
- id: my_route
uri: http://localhost:8080
#uri: https://baidu.com
predicates:
- Path=/**
filters:
- AddRequestHeadersIfNotPresent=X-Request-Color:blue, city:beijing # 只有第一个会生效
- AddRequestHeadersIfNotPresent=X-Request-Color:red, city:changchun # 不会生效
2.2 API式路由方式
@Configuration
public class GatewayConfig {
/**
* AddRequestHeadersIfNotPresent网关过滤工厂 —— API式
*
* @param builder
* @return
*/
@Bean
public RouteLocator filterAddRequestHeadersIfNotPresentRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("filterAddRequestHeadersIfNotPresent_route", predicateSpec -> predicateSpec.path("/**")
.filters(filterSpec -> filterSpec.addRequestHeadersIfNotPresent("X-Request-Color:black", "city:siping") // 只有这个会生效
.addRequestHeadersIfNotPresent("X-Request-Color:white", "city:lishu") // 不会生效,因为已经存在了
.addRequestHeadersIfNotPresent("X-Request-Color:green", "city:jilin")) // 不会生效
.uri("http://localhost:8080"))
.build();
}
}
三、AddRequestParameter过滤工厂
该过滤器工厂会对匹配上的请求添加指定的请求参数。
3.1 配置式路由方式
spring:
cloud:
gateway:
routes:
- id: my_route
uri: http://localhost:8080
#uri: https://baidu.com
predicates:
- Path=/**
filters:
- AddRequestParameter=color, blue
- AddRequestParameter=color, red
- AddRequestParameter=color, green
- AddRequestParameter=city, bj
- AddRequestParameter=fruit, apple
- AddRequestParameter=size, 123
3.2 API式路由方式
@Configuration
public class GatewayConfig {
/**
* AddRequestParameter网关过滤工厂 —— API式
*
* @param builder
* @return
*/
@Bean
public RouteLocator filterAddRequestParameterRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("filterAddRequestParameter_route", predicateSpec -> predicateSpec.path("/**")
.filters(filterSpec -> filterSpec.addRequestParameter("color", "blue")
.addRequestParameter("color", "red")
.addRequestParameter("fruit", "beijing")
.addRequestParameter("city", "jilin"))
.uri("http://localhost:8080"))
.build();
}
}
四、AddResponseHeader过滤工厂
该过滤器工厂会给从网关返回的响应添加上指定的请求头Header。
4.1 配置式路由方式
spring:
cloud:
gateway:
routes:
- id: my_route
uri: http://localhost:8080
#uri: https://baidu.com
predicates:
- Path=/**
filters:
- AddResponseHeader=X-Response-Color, Blue
- AddResponseHeader=X-Response-Color, Red
4.2 API式路由方式
@Configuration
public class GatewayConfig {
/**
* AddResponseHeader网关过滤工厂 —— API式
*
* @param builder
* @return
*/
@Bean
public RouteLocator filterAddResponseHeaderRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("filterAddResponseHeader_route", predicateSpec -> predicateSpec.path("/**")
.filters(filterSpec -> filterSpec.addResponseHeader("color", "blue")
.addResponseHeader("color", "red")
.addResponseHeader("fruit", "beijing")
.addResponseHeader("city", "jilin"))
.uri("http://localhost:8080"))
.build();
}
}
五、CircuitBreaker过滤工厂
该过滤器工厂完成网关层的服务熔断与降级。当访问的处理器不成功时,将服务降级到Gateway工程中定义的降级处理器上。
5.1 添加上resilience4j依赖
<!-- resilience4j依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
</dependency>
5.2 定义降级处理器
@RestController
public class FallbackController {
@RequestMapping("/fb")
public String fallbackHander() {
return "服务熔断后降级到了这个处理器!";
}
}
5.3 配置过滤器
5.3.1 配置式路由方式
spring:
cloud:
gateway:
routes:
- id: my_route
uri: http://localhost:8080
#uri: https://baidu.com
predicates:
- Path=/**
filters:
- name: CircuitBreaker # filter工厂名称
args:
name: myCircuitBreaker
fallbackUri: forward:/fb # 如果发生了服务熔断之后要进行服务降级,这是设置降级位置
-
name属性:用于指定要使用的过滤器Filter类型,不能随意写。
-
args属性:指定过滤器Filter的配置参数。
-
args属性的name属性:用于指定当前所使用断路器的名称,可以随意。
-
args属性的fallbackUri属性:用于指定发生断路后要提交的降级URI。
-
5.3.2 API式路由方式
@Configuration
public class GatewayConfig {
/**
* CircuitBreaker网关过滤工厂 —— API式
*
* @param builder
* @return
*/
@Bean
public RouteLocator filterCircuitBreakerRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("filterCircuitBreaker_route", predicateSpec -> predicateSpec.path("/**")
.filters(filterSpec -> filterSpec.circuitBreaker(config -> {
config.setName("myCircuitBreaker");
config.setFallbackUri("forward:/fb");
}))
.uri("http://localhost:8080"))
.build();
}
}
六、PrefixPath过滤工厂
该过滤器工厂会自动的为指定的URI前面添加上一个指定的URI前辍。
6.1 配置式路由方式
spring:
cloud:
gateway:
routes:
- id: my_route
uri: http://localhost:8081
predicates:
- Path=/depart/**
filters:
- PrefixPath=/provider
6.2 API式路由方式
@Configuration
public class GatewayConfig {
/**
* PrefixPath网关过滤工厂 —— API式
*
* @param builder
* @return
*/
@Bean
public RouteLocator filterPrefixPathRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("filterPrefixPath_route", predicateSpec -> predicateSpec.path("/depart/**")
.filters(filterSpec -> filterSpec.prefixPath("/provider"))
.uri("http://localhost:8081"))
.build();
}
}
七、StripPrefix过滤工厂
该过滤器工厂会为指定的URI去掉指定长度的前辍。
7.1 配置式路由方式
spring:
cloud:
gateway:
routes:
- id: my_route
uri: http://localhost:8081
predicates:
#- Path=/provider/depart/**
#- Path=/first/second/provider/depart/**
- Path=/*/*/provider/depart/**
filters:
- StripPrefix=2 # 指定去掉2个前辍
7.2 API式路由方式
@Configuration
public class GatewayConfig {
/**
* StripPrefix网关过滤工厂 —— API式
*
* @param builder
* @return
*/
@Bean
public RouteLocator filterStripPrefixRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
//.route("filterStripPrefix_route", predicateSpec -> predicateSpec.path("/first/second/provider/depart/**")
.route("filterStripPrefix_route", predicateSpec -> predicateSpec.path("/*/*/provider/depart/**")
.filters(filterSpec -> filterSpec.stripPrefix(2))
.uri("http://localhost:8081"))
.build();
}
}
八、RewritePath过滤工厂
该过滤器工厂会将请求URI替换为另一个指定的URI进行访问。
RewritePath有两个参数,第一个是正则表达式,第二个是要替换为的目标表达式。
8.1 配置式路由方式
spring:
cloud:
gateway:
routes:
- id: my_route
uri: http://localhost:8081
predicates:
#- Path=/red/blue/balck/**
- Path=/red/**
filters:
#- RewritePath=/red/blue/balck, /provider/depart
- RewritePath=/red/?(?<segment>.*), /$\{segment}
# localhost:9000/red/provider/depart/list ————> localhost:9000/provider/depart/list
# localhost:9000/red/wang/quan/97/list ————> localhost:9000/wang/quan/97/list
8.2 API式路由方式
@Configuration
public class GatewayConfig {
/**
* RewritePath网关过滤工厂 —— API式
*
* @param builder
* @return
*/
@Bean
public RouteLocator filterRewritePathRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("filterRewritePat_route", predicateSpec -> predicateSpec.path("/red/**")
.filters(filterSpec -> filterSpec.rewritePath("/red/?(?<segment>.*)", "/$\\{segment}"))
.uri("http://localhost:8081"))
.build();
}
}
九、RequestRateLimiterGateway过滤工厂
该过滤器工厂通过令牌桶算法对进来的请求进行限流。
9.1 添加Redis依赖
这个限流器是基于Redis的,所以需要导入Redis的Starter依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
9.2 添加限流键解析器
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
/**
* 限流key为用户
* 根据请求要访问的目标服务器的用户进行限流
*/
@Bean
KeyResolver userKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
}
/**
* 限流key为目标服务器的host/ip
* 根据请求要访问的目标服务器的host或ip进行限流
*/
@Bean
KeyResolver hostKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}
/**
* 限流key为URI
* 根据请求要访问的目标服务器的URI进行限流
*/
@Bean
KeyResolver pathKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getPath().value());
}
}
9.3 配置式路由方式
spring:
data:
redis:
host: 192.168.11.137
port: 6379
database: 1
cloud:
gateway:
routes:
- id: my_route
uri: http://localhost:8081
predicates:
- Path=/provider/depart/**
filters:
- name: RequestRateLimiter
args:
key-resolver: "#{@hostKeyResolver}"
redis-rate-limiter.replenishRate: 2 # 令牌桶算法中令牌每秒的填充速率
redis-rate-limiter.burstCapacity: 5 # 突发容量,请求突然增大时生成令牌的速率可以在原来基础上+5
redis-rate-limiter.requestedTokens: 1 # 每处理一个请求需要的令牌数量
十、默认Filters工厂
前面的网关过滤工厂是在某一特定路由策略中设置的,仅对这一种路由生效。若要使某些过滤效果应用到所有路由策略中,就可以将该网关过滤工厂定义在默认Filters中。
10.1 修改配置文件
default-filters下定义的路由为默认路由,而routers的filters下定义的路由则为局部路由。
spring:
data:
redis:
host: 192.168.11.137
port: 6379
database: 1
cloud:
gateway:
default-filters:
- name: CircuitBreaker
args:
name: myCircuitBreaker
fallbackUri: forward:/fb
routes:
- id: my_route1
uri: http://localhost:8080
predicates:
- Path=/bd/**
- id: my_route2
uri: http://localhost:8081
predicates:
- Path=/blbl/**
10.2 过滤工厂的优先级
对于相同的过滤工厂,在不同位置设置了不同的值,则优先级为:
- 局部过滤工厂的优先级高于默认过滤工厂
- API式的过滤工厂优先级高于配置式过滤工厂
十一、自定义网关过滤工厂
定义一个类, 该类类名由两部分构成:后面必须是GatewayFilterFactory,前面为功能前缀,该前缀将来要用在配置文件中。
@Component
public class AddHeaderGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
@Override
public GatewayFilter apply(NameValueConfig config) {
return ((exchange, chain) -> {
ServerHttpRequest changedRequest = exchange.getRequest()
.mutate() // 变更
.header(config.getName(), config.getValue())
.build();
ServerWebExchange changedExchange = exchange.mutate()
.request(changedRequest)
.build();
return chain.filter(changedExchange);
});
}
}
11.1 配置式路由方式
spring:
cloud:
gateway:
routes:
- id: my_route
uri: http://localhost:8080
predicates:
- Path=/info/**
filters:
- AddHeader=new-color, red
十二、GatewayFilter的pre与post
Gateway有“pre”和“post”两种方式的Filter。客户端的请求先按照Filter的优先级顺序(优先级相同,则按注册顺序),执行Filter的“pre”部分。然后将请求转发到相应的目标服务器,收到目标服务器的响应之后,再按照Filter的优先级顺序的逆序(优先级相同,则按注册顺序逆序),执行Filter的“post”部分,最后返回响应到客户端。
Filter的“pre”部分指的是chain.filter()方法执行之前的代码,而“post”部分,则是定义在chain.filter().then()方法中的代码。
12.1 定义过滤器
@Slf4j
@Component
public class OneGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
@Override
public GatewayFilter apply(NameValueConfig config) {
return ((exchange, chain) -> {
// pre-filter
long start = System.currentTimeMillis();
log.info(config.getName() + "——" + config.getValue() + "-pre,开始执行的时间:" + start);
exchange.getAttributes().put("startTime", start);
return chain.filter(exchange).then(
// post-filter
Mono.fromRunnable(() -> {
long startTime = (long) exchange.getAttributes().get("startTime");
long endTime = System.currentTimeMillis();
long elapsedTime = endTime - startTime;
log.info(config.getName() + "——" + config.getValue() + "-post,执行完毕的时间:" + endTime);
log.info("这个过滤器执行用时(毫秒):" + elapsedTime);
})
);
});
}
}
@Slf4j
@Component
public class TwoGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
@Override
public GatewayFilter apply(NameValueConfig config) {
return ((exchange, chain) -> {
// pre-filter
log.info(config.getName() + "——" + config.getValue() + "-pre");
return chain.filter(exchange).then(
// post-filter
Mono.fromRunnable(() -> {
log.info(config.getName() + "——" + config.getValue() + "-post");
})
);
});
}
}
@Slf4j
@Component
public class ThreeGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
@Override
public GatewayFilter apply(NameValueConfig config) {
return ((exchange, chain) -> {
// pre-filter
log.info(config.getName() + "——" + config.getValue() + "-pre");
return chain.filter(exchange).then(
// post-filter
Mono.fromRunnable(() -> {
log.info(config.getName() + "——" + config.getValue() + "-post");
})
);
});
}
}
12.2 修改配置文件
spring:
cloud:
gateway:
routes:
- id: my_route
uri: http://localhost:8080
predicates:
- Path=/info/**
filters: # 过滤器按顺序执行
- One=onefilter, 111
- Three=threefilter, 333
- Two=twofilter, 222
12.3 测试
@RestController
@RequestMapping("/info")
public class ShowInfoController {
@GetMapping("/time")
public String timeHandle() {
return "到达目标服务器的时间:" + System.currentTimeMillis();
}
}
十三、全局过滤器
全局过滤器Global Filter是应用于所有路由策略上的Filter。Gateway中已经定义好了很多GlobalFilter,但这些GlobalFilter无需任何的配置与声明,在符合应用条件时就会自动生效。
Global Filter不需要在任何具体的路由规则中进行注册,只需在类上添加@Compoment 注解,将其生命周期交给Spring容器来管理即可。
例:访问当前系统的任意模块的URL都需要是合法的URL,即请求中携带了token请求参数。
因为对所有请求的URL都要进行验证,所以这里就需要定义一个Global Filter,可以应用到所有路由中。
13.1 定义全局过滤器
@Component
public class UrlValidateGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 从请求中获取请求参数token的值
String token = exchange.getRequest().getQueryParams().getFirst("token");
// 如果token为空,则响应客户端状态码401,未授权。否则通过验证
if (!StringUtils.hasText(token)) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
// 为当前GlobalFilter赋予最高的优先级
return Ordered.HIGHEST_PRECEDENCE;
}
}
在浏览器进行访问自己项目中的任意处理器方法。如果请求中没有携带token请求参数,则验证失败。请求中携带了token参数,无论其值为什么均可通过验证。