Spring cloud gateway 使用

635 阅读4分钟

这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战

基本术语

  • Route(路由) :这是网关的基本构建块。它由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配。
  • Predicate(断言) :这是一个 Java 8 的 Predicate。输入类型是一个 ServerWebExchange。我们可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或参数。
  • Filter(过滤器) :这是org.springframework.cloud.gateway.filter.GatewayFilter的实例,我们可以使用它修改请求和响应。

断言 Predicate

Predicate(断言, 谓词) 用于进行条件判断,只有断言都返回真,才会真正的执行路由。

断言就是说: 在什么条件下才能进行路由转发。

在 Spring Cloud Gateway 中 Spring 利用 Predicate 的特性实现了各种路由匹配规则,有通过 Header、请求参数等不同的条件来进行作为条件匹配到对应的路由。

img

BetweenRoutePredicateFactory为例,使用方法如下:

application.yml

spring:  
    cloud:   
        gateway:      
            routes:      
            - id: between_route        
              uri: http://www.google.com        
              predicates:        
              - Between=2018-12-25T14:33:47.789+08:00, 2018-12-26T14:33:47.789+08:00

断言除了以上常见用法外,还有一方法ReadBodyPredicateFactory用于获取请求体Request Body信息,此方法没有被记录于官方文档,但在2.2.6RELEASE版本可用。

使用方法如下:

 @Bean
    public RouteLocator routes(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("test", r -> r.path("/test/path/**")
                        .and()
                        .readBody(String.class, requestBody -> true)
                        .filters(f -> f.filter(testGatewayFilterFactory.apply(new TestFactory.Config())))
                        .uri("lb://test"))
                .build();
    }

该方法通过装饰器模式,为exchange添加一个attributecachedRequestBodyObject 的方式,来实现获取请求体信息。(详见代码ServerWebExchangeUtils中的cacheRequestBody方法)

通过该断言处理后,直接获取exchangecachedRequestBodyObject即可获取到请求体信息。

        String body = exchange.getAttribute("cachedRequestBodyObject");

通过网上相关信息可知,目前已知缺陷主要有两点:

1、可能只能通过Java code的方式进行配置(未找到有效的yaml配置方法)。

2、当使用ReadBodyPredicateFactory读取request的body,可能无法匹配404。(详见CSDN

过滤器 filter

过滤器工厂 GatewayFilter Factories

Spring Cloud Gateway的路由过滤器允许以某种方式修改传入的HTTP请求或输出的HTTP响应。只作用于特定的路由。Spring Cloud Gateway中内置了很多的过滤器工厂,以便我们能够快速地实现相应常见的功能。

现已支持30种过滤器工厂,详见官方文档

过滤器工厂的使用方法有两种,一种是通过yaml的方式进行配置与使用,一种是通过Java code的方式进行配置与使用。

AddRequestHeader GatewayFilter factory为例,该工厂接受namevalue两个参数,从而为请求添加了Request Header

使用方法如下:

application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: https://example.org
        predicates:
        - Path=/red/{segment}
        filters:
        - AddRequestHeader=X-Request-Red, Blue-{segment}

ModifyRequestBody filter为例,该工厂在网关过滤器层面,修改请求体内容。

使用方法如下:

@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("rewrite_request_obj", r -> r.host("*.rewriterequestobj.org")
            .filters(f -> f.prefixPath("/httpbin")
                .modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE,
                    (exchange, s) -> return Mono.just(new Hello(s.toUpperCase())))).uri(uri))
        .build();
}
​
static class Hello {
    String message;
​
    public Hello() { }
​
    public Hello(String message) {
        this.message = message;
    }
​
    public String getMessage() {
        return message;
    }
​
    public void setMessage(String message) {
        this.message = message;
    }
}

全局过滤器 GlobalFilter

当请求匹配路由后,就会进入过滤器的执行链filter chainfilter chain包括全局过滤器GlobalFilter,还有设置了过滤某个接口的局部过滤器GatewayFilter

全局过滤器GlobalFilter,顾名思义就是对所有匹配路由成功后的请求进行过滤。全局过滤器需要实现GlobalFilterOrdered两个接口,同时加上注解@Component即可配置成功。其中,GlobalFilter代表全局过滤器,Ordered代表优先级,数字越小,优先级越大。

具体用法如下:

@Component
public class CustomGlobalFilter implements GlobalFilter, Ordered {
​
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("custom global filter");
        return chain.filter(exchange);
    }
​
    @Override
    public int getOrder() {
        return -1;
    }
}

局部过滤器 GatewayFilter

局部过滤器 GatewayFilter就是对指定匹配路由成功后的请求进行过滤。局部过滤器需要实现GatewayFilterOrdered两个接口,同时需要编写相应的工厂类生产局部过滤器(工厂类的名字需要符合XXGatewayFilterFactory的格式),最后将工厂类通过yaml或者Java code的方式进行配置即可。

具体用法如下:

局部过滤器:

@Component
public class TestFilter implements GatewayFilter, Ordered {
​
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        return chain.filter(exchange);
    }
​
    @Override
    public int getOrder() {
        return 0;
    }
}

局部过滤器工厂:

@Component
public class TestGatewayFilterFactory extends
        AbstractGatewayFilterFactory<CookieGatewayFilterFactory.Config> {
​
​
    @Autowired
    TestGatewayFilter testGatewayFilter;
​
​
    public TestGatewayFilterFactory() {
        super(TestGatewayFilterFactory.Config.class);
    }
​
    @Override
    public GatewayFilter apply(TestGatewayFilterFactory.Config config) {
        return testGatewayFilter;
    }
​
    public static class Config {
    }
}

yaml配置:

        - id: test123
          uri: lb://test123
          predicates:
            - Path=/test/**
          filters:
            - Test  #只需填写XXGatewayFilterFactory中的XX即可识别

Java code配置:

    @Autowired
    private CookieGatewayFilterFactory cookieGatewayFilterFactory;  
​
    @Bean
    public RouteLocator routes(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("test", r -> r.path("/test/path/**")
                        .filters(f -> f.filter(testGatewayFilterFactory.apply(new TestFactory.Config())))
                        .uri("lb://test"))
                .build();
    }