SpringCloudGateway--过滤器(内置filter)

目录

一、概览

二、内置过滤器

1、StripPrefix

2、AddRequestHeader

3、AddResponseHeader

4、DedupeResponseHeader

5、AddRequestParameter

6、CircuitBreaker

7、FallbackHeaders

8、RequestRateLimiter

9、RedirectTo

10、RemoveRequestHeader

11、RemoveResponseHeader

12、RemoveRequestParameter

13、RewritePath 

14、RewriteResponseHeader 

15、SaveSession

16、SecureHeaders

17、SetPath

18、SetRequestHeader

19、SetResponseHeader

20、SetStatus

21、PrefixPath

22、Retry

23、RequestSize

24、ModifyRequestBody

25、ModifyResponseBody

26、MapRequestHeader

27、PreserveHostHeader

28、RewriteLocationResponseHeader

29、SetRequestHostHeader


一、概览

        SpringGateway的过滤器分为内置过滤器Filter与自定义过滤器GlobalFilter。

        内置Filter都实现GatewayFilter接口。使用时filter

s属性中过滤器名为XXXGatewayFilterFactory的类对应的名称为XXX内置Filter都实现GatewayFilter接口。使用时filters属性中过滤器名为XXXGatewayFilterFactory的类对应的名称为XXX。其中内置过滤器实现类如下:

        同理在SpringCloudGateway中可以找到加载这些实现类的工厂方法: 

二、内置过滤器

         内置Filter是使用工厂模式加匿名内部类实现的。 

   

         所有的Filter最终一定要调用chain.filter()方法,代表向下执行,在这句话之前调用的逻辑,是微服务的前置过滤,在这之后的都是远程微服务调用的后置过滤。

        所有内置过滤器中,常用的四种已经标红,重点演示前四种。SpringCloudGateWay项目参考:SpringCloudGateway--自动路由映射与手动路由映射_雨欲语的博客-CSDN博客

1、StripPrefix

         StripPrefix是最常用的内置过滤器,含义是:过滤转发地址前缀, 也就是过滤掉url中前几节,然后转发给下游,比如以下配置:

routes: 
  - id: service-one 
    uri: lb://service-one
    predicates: 
      - Path=/service/**
    filters:
      - StripPrefix=1
    metadata:
      connect-timeout: 15000 #ms
      response-timeout: 15000 #ms

        当我们访问http://localhost:9999/service/nacos/test时,谓词校验service,uri的lb是service-one,此时转发地址是 http://service/service-one/nacos/test,然后后面有过滤器StripPrefix,表示删除第一节service,因此最终转发地址是http://service-one/nacos/test。

        我们看一下源码中的处理方式:

import java.util.Arrays;
import java.util.List;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.support.GatewayToStringStyler;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

public class StripPrefixGatewayFilterFactory extends AbstractGatewayFilterFactory<StripPrefixGatewayFilterFactory.Config> {
    public static final String PARTS_KEY = "parts";

    public StripPrefixGatewayFilterFactory() {
        super(StripPrefixGatewayFilterFactory.Config.class);
    }

    public List<String> shortcutFieldOrder() {
        return Arrays.asList("parts");
    }

    public GatewayFilter apply(StripPrefixGatewayFilterFactory.Config config) {
        return new GatewayFilter() {
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                ServerHttpRequest request = exchange.getRequest();
                ServerWebExchangeUtils.addOriginalRequestUrl(exchange, request.getURI());
                // 获取url的path
                String path = request.getURI().getRawPath();
                // 将url以/进行分割成字符串数组
                String[] originalParts = StringUtils.tokenizeToStringArray(path, "/");
                StringBuilder newPath = new StringBuilder("/");

                for(int i = 0; i < originalParts.length; ++i) {
                    // 如果当前索引下标大于配置,则添加到newPath中,否则相当于直接跳过
                    if (i >= config.getParts()) {
                        if (newPath.length() > 1) {
                            newPath.append('/');
                        }

                        newPath.append(originalParts[i]);
                    }
                }

                if (newPath.length() > 1 && path.endsWith("/")) {
                    newPath.append('/');
                }
                // 重新buildurl地址
                ServerHttpRequest newRequest = request.mutate().path(newPath.toString()).build();
                exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, newRequest.getURI());
                return chain.filter(exchange.mutate().request(newRequest).build());
            }

            public String toString() {
                return GatewayToStringStyler.filterToStringCreator(StripPrefixGatewayFilterFactory.this).append("parts", config.getParts()).toString();
            }
        };
    }

    public static class Config {
        private int parts;

        public Config() {
        }

        public int getParts() {
            return this.parts;
        }

        public void setParts(int parts) {
            this.parts = parts;
        }
    }
}

2、AddRequestHeader

        添加请求头参数,参数和值之间使用逗号分隔

routes: 
  - id: service-one 
    uri: lb://service-one
    predicates: 
      - Path=/service/**
    filters:
      - StripPrefix=1
      - AddRequestHeader=MyHeader,test
    metadata:
      connect-timeout: 15000 #ms
      response-timeout: 15000 #ms

        将之前的服务稍微修改一下,返回MyHeader:

@RestController
@RequestMapping("/nacos")
public class NacosTestController {

    @GetMapping("/test")
    public String test(@RequestHeader("MyHeader") String myHeader){
        return myHeader;
    }
}

        启动后访问:http://localhost:9999/service/nacos/test

        可以看到返回内容:

        源码就比较简单,就从配置文件中拿到header,然后添加进去即可:

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractNameValueGatewayFilterFactory.NameValueConfig;
import org.springframework.cloud.gateway.support.GatewayToStringStyler;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

public class AddRequestHeaderGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
    public AddRequestHeaderGatewayFilterFactory() {
    }

    public GatewayFilter apply(NameValueConfig config) {
        return new GatewayFilter() {
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                String value = ServerWebExchangeUtils.expand(exchange, config.getValue());
                ServerHttpRequest request = exchange.getRequest().mutate().headers((httpHeaders) -> {
                    httpHeaders.add(config.getName(), value);
                }).build();
                return chain.filter(exchange.mutate().request(request).build());
            }

            public String toString() {
                return GatewayToStringStyler.filterToStringCreator(AddRequestHeaderGatewayFilterFactory.this).append(config.getName(), config.getValue()).toString();
            }
        };
    }
}

3、AddResponseHeader

        在响应的header中添加参数

routes: 
  - id: service-one 
    uri: lb://service-one
    predicates: 
      - Path=/service/**
    filters:
      - StripPrefix=1
      - AddResponseHeader=addHeader, test
    metadata:
      connect-timeout: 15000 #ms
      response-timeout: 15000 #ms

        打开浏览器,F12显示控制台,访问之后可以看到响应的请求头中出现我们自己添加的数据

        源码也很简单,也是在响应的header中添加参数即可:

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractNameValueGatewayFilterFactory.NameValueConfig;
import org.springframework.cloud.gateway.support.GatewayToStringStyler;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

public class AddResponseHeaderGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
    public AddResponseHeaderGatewayFilterFactory() {
    }

    public GatewayFilter apply(NameValueConfig config) {
        return new GatewayFilter() {
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                String value = ServerWebExchangeUtils.expand(exchange, config.getValue());
                exchange.getResponse().getHeaders().add(config.getName(), value);
                return chain.filter(exchange);
            }

            public String toString() {
                return GatewayToStringStyler.filterToStringCreator(AddResponseHeaderGatewayFilterFactory.this).append(config.getName(), config.getValue()).toString();
            }
        };
    }
}

4、DedupeResponseHeader

        对指定响应头去重复,也即某个响应头key的value有多个值,去除一些重复的,有三种策略:

        RETAIN_FIRST 默认值,保留第一个

        RETAIN_LAST 保留最后一个

        RETAIN_UNIQUE 保留唯一的,出现重复的属性值,会保留一个。例如有两个My:bbb的属性,最后会只留一个。

     我们利用上面添加参数,添加两个key一样的,value不一样的header,然后利用DedupeResponseHeader进行去重:

routes: 
  - id: service-one 
    uri: lb://service-one
    predicates: 
      - Path=/service/**
    filters:
      - StripPrefix=1
      - AddResponseHeader=addHeader, test
      - AddResponseHeader=addHeader,test1
      - DedupeResponseHeader=addHeader, RETAIN_LAST
    metadata:
      connect-timeout: 15000 #ms
      response-timeout: 15000 #ms

         测试返回结果:

         这个的源码稍微复杂一点点,需要根据不同的策略进行选择不同的去重规则:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory.NameConfig;
import org.springframework.cloud.gateway.support.GatewayToStringStyler;
import org.springframework.http.HttpHeaders;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

public class DedupeResponseHeaderGatewayFilterFactory extends AbstractGatewayFilterFactory<DedupeResponseHeaderGatewayFilterFactory.Config> {
    private static final String STRATEGY_KEY = "strategy";

    public DedupeResponseHeaderGatewayFilterFactory() {
        super(DedupeResponseHeaderGatewayFilterFactory.Config.class);
    }

    public List<String> shortcutFieldOrder() {
        return Arrays.asList("name", "strategy");
    }

    public GatewayFilter apply(DedupeResponseHeaderGatewayFilterFactory.Config config) {
        return new GatewayFilter() {
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                // return中调用dedupe方法
                return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                    DedupeResponseHeaderGatewayFilterFactory.this.dedupe(exchange.getResponse().getHeaders(), config);
                }));
            }

            public String toString() {
                return GatewayToStringStyler.filterToStringCreator(DedupeResponseHeaderGatewayFilterFactory.this).append(config.getName(), config.getStrategy()).toString();
            }
        };
    }

    void dedupe(HttpHeaders headers, DedupeResponseHeaderGatewayFilterFactory.Config config) {
        String names = config.getName();
        DedupeResponseHeaderGatewayFilterFactory.Strategy strategy = config.getStrategy();
        if (headers != null && names != null && strategy != null) {
            // 根据空格进行分组,可以看出如果添加多个key,只需要使用空格隔开即可
            String[] var5 = names.split(" ");
            int var6 = var5.length;
            // 遍历需要去重的header
            for(int var7 = 0; var7 < var6; ++var7) {
                String name = var5[var7];
                this.dedupe(headers, name.trim(), strategy);
            }

        }
    }
    // 根据不同策略进行不同的去重策略
    private void dedupe(HttpHeaders headers, String name, DedupeResponseHeaderGatewayFilterFactory.Strategy strategy) {
        List<String> values = headers.get(name);
        if (values != null && values.size() > 1) {
            switch(strategy) {
            case RETAIN_FIRST:
                headers.set(name, (String)values.get(0));
                break;
            case RETAIN_LAST:
                headers.set(name, (String)values.get(values.size() - 1));
                break;
            case RETAIN_UNIQUE:
                headers.put(name, new ArrayList(new LinkedHashSet(values)));
            }

        }
    }

    public static class Config extends NameConfig {
        private DedupeResponseHeaderGatewayFilterFactory.Strategy strategy;

        public Config() {
            this.strategy = DedupeResponseHeaderGatewayFilterFactory.Strategy.RETAIN_FIRST;
        }

        public DedupeResponseHeaderGatewayFilterFactory.Strategy getStrategy() {
            return this.strategy;
        }

        public DedupeResponseHeaderGatewayFilterFactory.Config setStrategy(DedupeResponseHeaderGatewayFilterFactory.Strategy strategy) {
            this.strategy = strategy;
            return this;
        }
    }
    // 定义的三种策略
    public static enum Strategy {
        RETAIN_FIRST,
        RETAIN_LAST,
        RETAIN_UNIQUE;

        private Strategy() {
        }
    }
}

5、AddRequestParameter

        添加请求参数

routes: 
  - id: service-one 
    uri: lb://service-one
    predicates: 
      - Path=/service/**
    filters:
      - StripPrefix=1
      - AddRequestParameter=name,test
    metadata:
      connect-timeout: 15000 #ms
      response-timeout: 15000 #ms

6、CircuitBreaker

        实现熔断时使用,支持CircuitBreaker和Hystrix两种,这个功能会单开一篇文章阐述。

7、FallbackHeaders

        添加降级时的异常信息,一般与CircuitBreaker配合使用。

8、RequestRateLimiter

         限流,会单开文章阐述。地址:SpringCloudGateway--基于redis实现令牌桶算法_雨欲语的博客-CSDN博客

9、RedirectTo

        重定向,连个参数,status和url,status是重定向300系列状态码

routes: 
  - id: service-one 
    uri: lb://service-one
    predicates: 
      - Path=/service/**
    filters:
      - StripPrefix=1
      - RedirectTo=302, https://www.baidu.com
    metadata:
      connect-timeout: 15000 #ms
      response-timeout: 15000 #ms

10、RemoveRequestHeader

        删除请求头

routes: 
  - id: service-one 
    uri: lb://service-one
    predicates: 
      - Path=/service/**
    filters:
      - StripPrefix=1
      - AddRequestHeader=MyHeader, test
      - RemoveRequestHeader=MyHeader
    metadata:
      connect-timeout: 15000 #ms
      response-timeout: 15000 #ms

11、RemoveResponseHeader

        删除响应头

routes: 
  - id: service-one 
    uri: lb://service-one
    predicates: 
      - Path=/service/**
    filters:
      - StripPrefix=1
      - AddResponseHeader=addHeader, test
      - RemoveResponseHeader=addHeader
    metadata:
      connect-timeout: 15000 #ms
      response-timeout: 15000 #ms

12、RemoveRequestParameter

        删除请求参数

routes: 
  - id: service-one 
    uri: lb://service-one
    predicates: 
      - Path=/service/**
    filters:
      - StripPrefix=1
      - RemoveRequestParameter=name
    metadata:
      connect-timeout: 15000 #ms
      response-timeout: 15000 #ms

13、RewritePath 

        重写请求路径,比如我们之前都是访问http://localhost:9999/service/nacos/test,现在我们重写路径,并访问http://localhost:9999/service/test1/test,gateway会帮助我们将test1换成nacos

routes: 
  - id: service-one 
    uri: lb://service-one
    predicates: 
      - Path=/service/**
    filters:
      - StripPrefix=1
      - RewritePath=/test1/?(?<segment>.*), /nacos/$\{segment}
    metadata:
      connect-timeout: 15000 #ms
      response-timeout: 15000 #ms

14、RewriteResponseHeader 

        修改响应的header,三个参数,一是key,二是value,可用正则,三是修改value的结果:

routes: 
  - id: service-one 
    uri: lb://service-one
    predicates: 
      - Path=/service/**
    filters:
      - StripPrefix=1
      - AddResponseHeader=addHeader, test1
      - RewriteResponseHeader=addHeader,test1,test
    metadata:
      connect-timeout: 15000 #ms
      response-timeout: 15000 #ms

15、SaveSession

        如果项目中使用Spring Security和Spring Session整合时,想确保安全信息都传到下游机器,需要使用此Filter,在转发到后端微服务请求之前,强制执行WebSession::Save操作。用在那种像Spring session延迟数据存储的,并希望在请求转发前确保session状态保存情况。

routes: 
  - id: service-one 
    uri: lb://service-one
    predicates: 
      - Path=/service/**
    filters:
      - StripPrefix=1
      - SaveSession
    metadata:
      connect-timeout: 15000 #ms
      response-timeout: 15000 #ms

16、SecureHeaders

        在响应的头中添加很多安全相关的信息:

routes: 
  - id: service-one 
    uri: lb://service-one
    predicates: 
      - Path=/service/**
    filters:
      - StripPrefix=1
      - SecureHeaders
    metadata:
      connect-timeout: 15000 #ms
      response-timeout: 15000 #ms

        也可以让某些信息不显示,需要进行配置,比如我关闭strict-transport-security和x-download-options:

filter:
  secure-headers:
    disable:
      - strict-transport-security
      - x-download-options
routes: 
  - id: service-one 
    uri: lb://service-one
    predicates: 
      - Path=/service/**
    filters:
      - StripPrefix=1
      - SecureHeaders
    metadata:
      connect-timeout: 15000 #ms
      response-timeout: 15000 #ms

17、SetPath

        功能和StripPrefix有点类似,语法更贴近restful,是将predicates中的路径进行修改:

routes: 
  - id: service-one 
    uri: lb://service-one
    predicates: 
      - Path=/service/{segment}
    filters:
      - SetPath=/{segment}
    metadata:
      connect-timeout: 15000 #ms
      response-timeout: 15000 #ms

18、SetRequestHeader

        设置请求头header,将指定的key的value值修改为指定的value,如果不存在就新建:

routes: 
  - id: service-one 
    uri: lb://service-one
    predicates: 
      - Path=/service/**
    filters:
      - SetRequestHeader=myHeader, test
    metadata:
      connect-timeout: 15000 #ms
      response-timeout: 15000 #ms

19、SetResponseHeader

        设置响应的header,将指定的key的value值修改为指定的value,如果不存在就新建:

routes: 
  - id: service-one 
    uri: lb://service-one
    predicates: 
      - Path=/service/**
    filters:
      - SetResponseHeader=addHeader, test
    metadata:
      connect-timeout: 15000 #ms
      response-timeout: 15000 #ms

20、SetStatus

        设置返回的code:

routes: 
  - id: service-one 
    uri: lb://service-one
    predicates: 
      - Path=/service/**
    filters:
      - SetStatus=500
    metadata:
      connect-timeout: 15000 #ms
      response-timeout: 15000 #ms

21、PrefixPath

        给请求路径path添加前缀:

routes: 
  - id: service-one 
    uri: lb://service-one
    predicates: 
      - Path=/service/**
    filters:
      - StripPrefix=1
      - PrefixPath=/nacos
    metadata:
      connect-timeout: 15000 #ms
      response-timeout: 15000 #ms

22、Retry

        设置重试次数:

        ①retries:重试次数
        ②statuses:遇到什么样的返回状态才重试,取值参考:org.springframework.http.HttpStatus
        ③methods:那些类型的方法会才重试(GET、POST等),取值参考:org.springframework.http.HttpMethod
        ④series:遇到什么样的series值才重试,取值参考:org.springframework.http.HttpStatus.Series
        ⑤exceptions:遇到什么样的异常才重试
        ⑥backoff:重试策略,由多个参数构成,例如firstBackoff

routes: 
  - id: service-one 
    uri: lb://service-one
    predicates: 
      - Path=/service/**
    filters:
      - StripPrefix=1
      - name: Retry
        args:
          retries: 3
          statuses: BAD_GATEWAY
          methods: GET,POST
          backoff:
            firstBackoff: 10ms
            maxBackoff: 50ms
            factor: 2
            basedOnPreviousValue: false
    metadata:
      connect-timeout: 15000 #ms
      response-timeout: 15000 #ms

23、RequestSize

        控制请求大小,单位有KB、MB、B,默认是B,如果没有设置,默认上限是5MB:

routes: 
  - id: service-one 
    uri: lb://service-one
    predicates: 
      - Path=/service/**
    filters:
      - StripPrefix=1
      - RequestSize=200
    metadata:
      connect-timeout: 15000 #ms
      response-timeout: 15000 #ms

24、ModifyRequestBody

        修改请求体body内容,官方推荐使用代码完成。

25、ModifyResponseBody

        修改响应body的内容,也是推荐使用代码完成。

26、MapRequestHeader

        用于键值对的赋值,以下意思是如果请求的header中有myHeader,就新增myHeader1,值和myHeader一样:

routes: 
  - id: service-one 
    uri: lb://service-one
    predicates: 
      - Path=/service/**
    filters:
      - StripPrefix=1
      - MapRequestHeader=myHeader, myHeader1
    metadata:
      connect-timeout: 15000 #ms
      response-timeout: 15000 #ms

        如果原有的header中已经有myHeader1,同时也有myheader,那么会新增一个myHeader1,值和myHeader一样。 

27、PreserveHostHeader

        在转发请求到服务提供者时,保留host信息:

routes: 
  - id: service-one 
    uri: lb://service-one
    predicates: 
      - Path=/service/**
    filters:
      - StripPrefix=1
      - PreserveHostHeader
    metadata:
      connect-timeout: 15000 #ms
      response-timeout: 15000 #ms

28、RewriteLocationResponseHeader

        用于改写reponse中的location信息,一共四个参数:stripVersionMode、locationHeaderName、hostValue、protocolsRegex,其中stripVersionMode策略一共三种:        

        NEVER_STRIP:不执行
        AS_IN_REQUEST :原始请求没有vesion,就执行
        ALWAYS_STRIP :固定执行

        Location用于替换host:port部分,如果没有就是用Request中的host;protocolsRegex用于匹配协议,如果匹配不上,name过滤器啥都不做

routes: 
  - id: service-one 
    uri: lb://service-one
    predicates: 
      - Path=/service/**
    filters:
      - StripPrefix=1
      - RewriteLocationResponseHeader=AS_IN_REQUEST, Location, ,
    metadata:
      connect-timeout: 15000 #ms
      response-timeout: 15000 #ms

29、SetRequestHostHeader

        修改请求header中的host值:

routes: 
  - id: service-one 
    uri: lb://service-one
    predicates: 
      - Path=/service/**
    filters:
      - StripPrefix=1
      - SetRequestHostHeader=name,test
    metadata:
      connect-timeout: 15000 #ms
      response-timeout: 15000 #ms

猜你喜欢

转载自blog.csdn.net/qq_41061437/article/details/128069622