美文网首页Spring Cloud Gateway
Spring Cloud Gateway GatewayFilt

Spring Cloud Gateway GatewayFilt

作者: huan1993 | 来源:发表于2020-11-01 23:40 被阅读0次

    一、GatewayFilter的作用

    路由过滤器允许我们以某种方式修改进来的Request和出去的Response。Spring Cloud Gateway内置很多的 GatewayFilter

    二、Spring Cloud Gateway内置的 GatewayFilter

    1、AddRequestHeader

    1、描述

    1、用于向下游服务 添加 请求头,
    2、支持 uri variables

    2、参数

    1、name:向下游服务传递请求头的 key
    2、value:向下游服务传递请求头的 value

    3、示例

    1、方式一、添加一个固定的请求头

    spring:
      cloud:
        nacos:
        gateway:
          routes:
            - id: product-provider-01
              uri: lb://product-provider
              predicates:
                - Path=/product/findOne
              filters:
                - AddRequestHeader=x-token,xxxxx
    

    表示会向下游服务传递一个 x-token 的请求头,值是 xxxxx

    2、配合 uri variables 添加动态请求头

    spring:
      cloud:
        gateway:
          routes:
            - id: product-provider-02
              uri: lb://product-provider
              predicates:
                - Path=/product/findOne/{productId}
              filters:
                - AddRequestHeader=x-token,xxxxx-{productId}
    

    表示会向下游服务传递一个 x-token 的请求头,值是 xxxxx-匹配上的productId的值

    2、AddRequestParameter

    1、描述

    用于向下游服务 添加一个请求参数

    2、参数

    1、name:添加的参数 key
    2、value:添加的参数 value,可以支持 Path或Host 中的 uri variables

    3、示例

    spring:
      cloud:
        gateway:
          routes:
            - id: product-provider-01
              uri: lb://product-provider
              predicates:
                - Path=/product/findOne
              filters:
                - AddRequestParameter=username,zhangsan
    

    向下游服务增加一个 username = zhangsan 的请求参数

    3、AddResponseHeader

    1、描述

    向下游的响应中增加一个 响应头。

    2、参数

    1、name:添加的响应头的 key
    2、value:添加的响应头的 value,可以支持 Path或Host 中的 uri variables

    3、示例

    spring:
      cloud:
        gateway:
          routes:
            - id: product-provider-01
              uri: lb://product-provider
              predicates:
                - Path=/product/findOne
              filters:
                - AddResponseHeader=encryption,false
    

    向下游服务响应增加一个 encryption = false 的响应头

    4、DedupeResponseHeader

    1、描述

    移除重复的请求头

    2、参数

    1、name:需要移除的重复的响应头,多个以 空格 分隔
    2、strategy:重复时,移除的策略,默认是RETAIN_FIRST,即保留第一个头

    1. RETAIN_FIRST:保留第一个值
    2. RETAIN_LAST:保留最后一个值
    3. RETAIN_UNIQUE:保留所有的不重复的值

    3、示例

    spring:
      cloud:
        gateway:
          routes:
            - id: product-provider-01
              uri: lb://product-provider
              predicates:
                - Path=/product/findOne
              filters:
                - DedupeResponseHeader=x-token Access-Control-Allow-Credentials Access-Control-Allow-Origin,RETAIN_FIRST
    

    移除上方指定的重复的响应头,并且保留第一个出现的。

    5、MapRequestHeader

    1、描述

    fromHeader 参数的值 追加到 toHeader 参数中。

    1. fromHeader 在 header 中不存在,那么没有什么影响。
    2. fromHeader 存在
    3.  toHeader 存在,那么往配置中 toHeader 对应的 header 中追加 fromHeader对应的值 
      
    4. toHeader 不存在,那么往 header 中增加一个header ,key: 配置中的toHeader的值,value: fromHeader 在header中的值
      

    2、参数

    1、fromHeader:从请求参数中获取header的值
    2、toHeader:向header中设置一个 header, key是toheader的值,value 是 fromHeader的值

    3、示例

    spring:
      cloud:
        gateway:
          routes:
            - id: product-provider-01
              uri: lb://product-provider
              predicates:
                - Path=/product/findOne
              filters:
                - MapRequestHeader=from-header,x-token
    

    即会向 request 中增加一个 key 为 x-token 的header ,值为 header 中 from-header 对应的值。(存在多种情况,参考描述

    6、Prefix

    1、描述

    为匹配到的路由,在转发到下游服务时,增加一个前缀prefix

    2、参数

    1、prefix:需要增加的路径前缀

    3、示例

    spring:
      cloud:
        gateway:
          routes:
            - id: product-provider-01
              uri: lb://product-provider
              predicates:
                - Path=/findOne
              filters:
                - PrefixPath=/product
    

    访问 http://网关ip:port/findOne ⇒ 转发到下游服务地址 http://product-provider/product/findOne 增加了一个前缀。

    7、PreserveHostHeader

    1、描述

    它表示在Spring Cloud Gateway转发请求的时候,保持客户端的Host信息不变,然后将它传递到下游服务器中。

    2、参数

    没有参数

    3、示例

    spring:
      cloud:
        gateway:
          routes:
            - id: product-provider-01
              uri: lb://product-provider
              predicates:
                - Path=/product/findOne
              filters:
                - PreserveHostHeader
    

    8、RemoveRequestHeader

    1、描述

    移除请求头中的参数

    2、参数

    1、name:需要移除的请求头

    3、示例

    spring:
      cloud:
        gateway:
          routes:
            - id: product-provider-01
              uri: lb://product-provider
              predicates:
                - Path=/product/findOne
              filters:
                - RemoveRequestHeader=x-token
    

    9、RemoveResponseHeader

    1、描述

    移除响应头

    2、参数

    1、name:需要移除的响应头

    3、示例

    spring:
      cloud:
        gateway:
          routes:
            - id: product-provider-01
              uri: lb://product-provider
              predicates:
                - Path=/product/findOne
              filters:
                - RemoveResponseHeader=x-token
    

    9、RemoveRequestParameter

    1、描述

    移除请求参数,往下游服务传递时,此参数就没有来

    2、参数

    1、name:需要移除的请求参数

    3、示例

    spring:
      cloud:
        gateway:
          routes:
            - id: product-provider-01
              uri: lb://product-provider
              predicates:
                - Path=/product/findOne
              filters:
                - RemoveRequestParameter=password
    

    10、RewritePath

    1、描述

    根据正则表达式,执行路径重写

    2、参数

    1、regexp:匹配的正则表达式
    2、replacement:需要替换成的字符串
    注意:
    1、在yml配置中 $ 需要写成 $\
    2、路径替换规则是: path.replaceAll(regexp,replacement)

    3、示例

    spring:
      cloud:
        gateway:
          routes:
            - id: product-provider-01
              uri: lb://product-provider
              predicates:
                - Path=/admin/product/findOne
              filters:
                - RewritePath=/admin(?<segment>/?.*), $\{segment} # 当访问/admin/product/findOne 将会替换成 /product/findOne
    

    页面上访问 /admin/product/findOne ⇒ 到达下游服务的路径是 /product/findOne

    11、StripPrefix

    1、描述

    移除路径前缀,比如访问: /admin/aa/bb/cc 实际的下游服务器地址是 /bb/cc 则可以使用这个实现

    2、参数

    1、parts:请求的路径按照/分隔后,需要跳过的部分,从1开始。

    3、示例

    spring:
      cloud:
        gateway:
          routes:
            - id: product-provider-01
              uri: lb://product-provider
              predicates:
                - Path=/admin/product/findOne
              filters:
                - StripPrefix=1 # 当访问/admin/product/findOne 将会替换成 /product/findOne
    

    页面上访问 /admin/product/findOne ⇒ 到达下游服务的路径是 /product/findOne

    12、RequestSize

    1、描述

    配置请求体的大小,当请求体过大时,将会返回 413 Payload Too Large

    2、参数

    1、maxSize:请求体的大小

    3、示例

    spring:
      cloud:
        gateway:
          routes:
            - id: product-provider-01
              uri: lb://product-provider
              predicates:
                - Path=/product/findOne
              filters:
                - name: RequestSize
                  args:
                    maxSize: 1B
    

    此处需要通过 filters[index].args.maxSize 配置,否则不生效。

    13、ModifyRequestBody

    1、描述

    修改传递到下游服务 RequestBody 的值,比如我们所有的经过网关的服务,到达下游服务时,都需要将 用户当前的用户名和数据权限传递下去,此时就可以使用这个。

    2、需求:

    修改原始服务的参数,增加usernameroles参数传递到下游服务。

    3、路由配置,只可通过 Java 代码来配置

    @Configuration
    public class RouteConfig {
    
        @Bean
        public RouteLocator routes(RouteLocatorBuilder builder) {
            return builder.routes()
                    .route("product-provider", predicateSpec -> predicateSpec.path("/product/modifyRequestBody")
                            .filters(gatewayFilterSpec -> gatewayFilterSpec.modifyRequestBody(String.class, Map.class, MediaType.APPLICATION_JSON_VALUE, (exchange, s) -> {
                                Map<String, Object> params = new HashMap<>(16);
                                params.put("old", s);
                                params.put("username", "v_huan");
                                params.put("roles", "ROLE_ADMIN");
                                return Mono.just(params);
                            })).uri("lb://product-provider")).build();
        }
    }
    
    修改RequestBody参数

    14、ModifyResponseBody

    1、 描述

    修改下游服务返回的ResponseBody的值,和上方的RequestBody类似,此处不在处理。

    15、更多的 GatewayFilterFactory

    在代码中查看 GatewayFilterFactory 的子类即可。

    三、配置默认拦截器

    1、描述

    默认拦截器,对所有的路由都会生效。

    2、配置方法

    配置文件中使用 spring.cloud.gateway.default-filters 配置

    3、示例

    spring:
      cloud:
        gateway:
          default-filters:
          - AddResponseHeader=X-Response-Default-Red, Default-Blue
          - PrefixPath=/httpbin
    

    四、全局过滤器

    当某个请求被路由匹配时,那么所有的全局过滤器(GlobalFilter)和路由匹配到的 GatewayFilter会组合成一个过滤器链,排序规则是通过 Spring 的 Ordered 来排序。

    GlobalFilterprepost2个执行阶段,优先级越高 pre 阶段执行越早, post阶段执行越迟。

    编写一个全局过滤器需要实现 GlobalFilter 接口。
    注意:
    [图片上传失败...(image-2163c0-1604245177902)]
    GlobalFilter的用来在未来的版本中可能会发生改变。

    1、ForwardRoutingFilter

    ForwardRoutingFilter 用来处理 forward:///localendpoint 请求。

    2、LoadBalancerClientFilter

    LoadBalancerClientFilter 用来处理 lb://service-name 这样的请求

    spring:
      cloud:
        gateway:
          routes:
          - id: myRoute
            uri: lb://service
            predicates:
            - Path=/service/**
    
    推荐使用ReactiveLoadBalancerClientFilter
    设置 spring.cloud.loadbalancer.ribbon.enabled=false

    3、The Websocket Routing Filter

    处理 websocket 请求

    spring:
      cloud:
        gateway:
          routes:
          # SockJS route
          - id: websocket_sockjs_route
            uri: http://localhost:3001
            predicates:
            - Path=/websocket/info/**
          # Normal Websocket route
          - id: websocket_route
            uri: [ws|wss]://localhost:3001
            predicates:
            - Path=/websocket/**
    

    4、更多全局过滤器

    https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#global-filters

    五、自定义全局过滤器

    1. 实现 GlobalFilter 接口和 Ordered 接口
    2. 加入到 Spring 管理
    3. 代码实现
    @Slf4j
    @Component
    public class CustomGlobalFilter implements GlobalFilter, Ordered {
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            String path = exchange.getRequest().getPath().toString();
            log.info("进入了全局过滤器,path:[{}]", path);
    
            // 修改 request
            ServerHttpRequest request = exchange.getRequest().mutate().header("x-token", "123456").build();
    
            return chain.filter(exchange.mutate().request(request).build());
        }
    
        /**
         * 指定全局过滤器执行的顺序
         *
         * @return
         */
        @Override
        public int getOrder() {
            return 0;
        }
    }
    

    六、编写一个 GatewayFilter

    1、实现 GatewayFilterFactory 或者继承 AbstractGatewayFilterFactory<C>
    2、继承AbstractGatewayFilterFactory<C> 类时,构造方法中需要调用 super(C.class)
    3、重写 shortcutFieldOrder方法,定义配置文件中字段的位置
    4、编写的过滤器类必须要以 GatewayFilterFactory结尾
    5、自定义过滤器的名字为,GatewayFilterFactory前面的字符,比如 TokenGatewayFilterFactory,那么过滤器名字是 Token
    5、功能实现:过滤器实现,从请求中获取一个参数的值,打印出来

    1、Java代码实现

    @Component
    @Slf4j
    public class TokenGatewayFilterFactory extends AbstractGatewayFilterFactory<TokenGatewayFilterFactory.Config> {
    
        public TokenGatewayFilterFactory() {
            super(Config.class);
        }
    
        @Override
        public List<String> shortcutFieldOrder() {
            return Lists.newArrayList("tokenName");
        }
    
        @Override
        public GatewayFilter apply(Config config) {
            return (exchange, chain) -> {
                log.info("每次路由匹配到会执行");
                String tokenName = config.getTokenName();
                log.info("从配置文件中获取到的 tokenName 的值=[{}].", tokenName);
                String value = exchange.getRequest().getQueryParams().getFirst(tokenName);
                log.info("从请求中获取到的token value 是:[{}]", value);
                return chain.filter(exchange);
            };
        }
    
        @Data
        public static class Config {
            private String tokenName;
        }
    }
    

    2、配置文件中的写法

    spring:
      cloud:
        gateway:
          routes:
            - id: product-provider-01
              uri: lb://product-provider
              predicates:
                - Path=/product/findOne
              filters:
                - Token=gateway-token
    

    七、其它的配置

    1、获取当前命令的路由

     // 1、获取当前匹配上的路由
    Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
    String routeId = route.getId();
    Map<String, Object> metadata = route.getMetadata();
    log.info("routeId:[" + routeId + "]" + "metadata:[" + metadata + "]");
    

    2、过滤器的执行顺序

    filter 的执行顺序

    3、配置超时时间

    1、配置全局超时时间(注意单位)

    spring:
      cloud:
        gateway:
          httpclient:
            # 全局的连接超时,单位毫秒
            connect-timeout: 1000
            # 全局的响应超时,需要符合java.time.Duration规范
            response-timeout: 3s
    

    2、配置每个路由的超时时间(注意单位)

    spring:
      cloud:    
        gateway:
          routes:
            - id: product-provider-01
              uri: lb://product-provider
              predicates:
                - Path=/product/findOne
              metadata:
                # 单个路由配置响应超时,单位毫秒
                response-timeout: 200
                # 单个路由配置连接超时,单位毫秒
                connect-timeout: 200
    

    4、输出gateway调试日志

    1、输出 netty 的 access log

    需要使用设置如下参数,注意是 JVM 的参数

    -Dreactor.netty.http.server.accessLogEnabled=true
    

    2、设置日志级别 debug or trace

    logging:
      level:
        org.springframework.cloud.gateway: trace
        org.springframework.http.server.reactive: trace
        org.springframework.web.reactive: trace
        org.springframework.boot.autoconfigure.web: trace
        reactor.netty: trace
    

    八、网关错误的处理

    实现 WebExceptionHandler 接口,在里面出现错误逻辑。需要注意 指定该接口的 order 否则可能被别的处理了,自己的不生效。
    返回完成信号:表示错误处理完成
    返回错误信息:交由下个处理器处理。
    eg:

    /**
     * 异常处理器,需要注意 @Order 指定执行的顺序
     *
     * @author huan.fu 2020/11/15 - 14:47
     */
    @Component
    @Slf4j
    @Order(Integer.MIN_VALUE)
    public class GatewayExceptionHandler implements WebExceptionHandler {
    
        @Override
        public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
            log.info("发生了异常", ex);
            String errorMsg = "{\"code\":500,\"msg\":\"服务器内部错误\"}";
    
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
            response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
            DataBuffer dataBuffer = response.bufferFactory().wrap(errorMsg.getBytes(StandardCharsets.UTF_8));
            return response.writeWith(Mono.just(dataBuffer));
        }
    }
    

    九、完整代码

    https://gitee.com/huan1993/spring-cloud-alibaba-parent/tree/master/gateway-filter

    十、参考链接

    https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#gatewayfilter-factories

    相关文章

      网友评论

        本文标题:Spring Cloud Gateway GatewayFilt

        本文链接:https://www.haomeiwen.com/subject/onhpvktx.html