开发依赖 | 版本 |
---|---|
Spring Boot | 2.7.0 |
Spring Cloud | 2021.0.1 |
Spring Cloud Alibaba | 2021.0.1.0 |
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webfluxartifactId>
dependency>
dependencies>
以下是一个简单的转发的配置,把/baidu
的请求转发到https://www.baidu.com
server:
port: 8081
spring:
application:
name: demo-gateway
cloud:
gateway:
#路由配置
routes:
- id: baidu
uri: https://www.baidu.com
predicates:
- Path=/baidu
filters:
- StripPrefix=1
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(DemoGatewayApplication.class, args);
}
}
程序启动后输出以下日志,说明程序启动成功
Started DemoGatewayApplication in 16.408 seconds (JVM running for 19.345)
在浏览器地址栏输入http://localhost:8081/baidu
Spring Cloud Gateway内置了多种过滤器,我们示例中演示了StripPrefix过滤器的使用,把原始请求/baidu
截断了,这样转发到http://localhost:8081/baidu
时才不会出现404错误
Spring Cloud Gateway虽然自带有许多实用的GatewayFilter Factory、Gateway Filter、Global Filter,但是在很多业务情景下仍然需要自定义过滤器,实现一些自定义操作,满足业务需求。所以自定义过滤器就显得非常有必要。本文分表介绍自定义Gateway Filter、自定义Global Filter、自定义Gateway Filter Factory。
实现自定义的Gateway Filter,需要实现GatewayFilter、Ordered两个接口
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@RequiredArgsConstructor
@Slf4j
public class DemoGatewayFilter implements GatewayFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.debug("this is GatewayFilter!");
return chain.filter(exchange);
}
@Override
public int getOrder() {
// 数值越小,越先执行
return 0;
}
}
定义好DemoGatewayFilter以后,需要跟Route绑定使用,不能在application.yml文件中配置使用
@Configuration
public class FilterAutoConfiguration {
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes().route(r ->
r.path("/**")
.filters(gfs -> gfs.filters(new DemoGatewayFilter()))
.uri("https://www.baidu.com"))
.build();
}
}
测试结果: 可以在控制台看到输出this is GatewayFilter!
,但会报404,因为/baidu
的请求转发后最终请求的是https://www.baidu.com/baidu
很多时候更希望在配置文件中配置Gateway Filter, 像使用gateway内置的过滤器那样,所以可以自定义过滤器工厂实现。稍微改造下上面的DemoGatewayFilter
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@RequiredArgsConstructor
@Slf4j
public class DemoGatewayFilter implements GatewayFilter, Ordered {
private final Config config;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
if (!config.isEnabled()) {
return chain.filter(exchange);
}
log.debug("this is DemoGatewayFilterFactory!");
log.debug("args: {}, {}, {}", config.isEnabled(), config.getArg1(), config.getArg2());
exchange.getAttributes().put("begin_time", System.currentTimeMillis());
return chain.filter(exchange).then(
Mono.fromRunnable(() -> {
Long beginTime = exchange.getAttribute("begin_time");
log.info("begin:{}, end:{}", beginTime, System.currentTimeMillis());
})
);
}
@Override
public int getOrder() {
// 数值越小,越先执行
return 0;
}
@Data
public static class Config {
private boolean enabled;
private String arg1;
private String arg2;
}
}
注释掉上面测试时绑定的Route
@Configuration
public class FilterAutoConfiguration {
// @Bean
// public RouteLocator routeLocator(RouteLocatorBuilder builder) {
// return builder.routes().route(r ->
// r.path("/**")
// .filters(gfs -> gfs.filters(new DemoGatewayFilter1()))
// .uri("https://www.baidu.com"))
// .build();
// }
}
自定义过滤器工厂需要继承AbstractGatewayFilterFactory
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Slf4j
@Component
public class DemoGatewayFilterFactory extends AbstractGatewayFilterFactory<DemoGatewayFilter.Config> {
public DemoGatewayFilterFactory() {
super(DemoGatewayFilter.Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
// yml配置文件中参数的赋值顺序
return Arrays.asList("enabled", "arg1", "arg2");
}
@Override
public GatewayFilter apply(DemoGatewayFilter.Config config) {
return new DemoGatewayFilter(config);
}
}
在application.yml配置使用
server:
port: 8081
spring:
application:
name: demo-gateway
cloud:
gateway:
#路由配置
routes:
- id: baidu
uri: https://www.baidu.com
predicates:
- Path=/baidu
filters:
- StripPrefix=1
- Demo=true,hello,world
测试结果: 控制台可以看到输出
this is DemoGatewayFilterFactory!
args: true, hello, world
实现自定义全局过滤器需要实现GlobalFilter和Ordered
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Slf4j
@Component
public class DemoGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.debug("this is DemoGlobalFilter");
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
同一种filter可以配置多次,配置方式也有多种,以下是不同方式的示例
server:
port: 8081
spring:
application:
name: demo-gateway
cloud:
gateway:
#路由配置
routes:
- id: baidu
uri: https://www.baidu.com
predicates:
- Path=/baidu
filters:
- StripPrefix=1
- Demo=true,hello,world
- name: Demo
args:
enabled: true
arg1: hello2
arg2: world2
示例演示了启动一个Spring Cloud Gateway需要的最小依赖,以及程序配置。通过自定义Filter可以满足我们个性化的业务需求,希望可以帮助大家快速基于Gateway做二次开发。