0


SpringCloudGateway--过滤器(内置filter)

一、概览

    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
标签: gateway java

本文转载自: https://blog.csdn.net/qq_41061437/article/details/128069622
版权归原作者 雨欲语 所有, 如有侵权,请联系我们删除。

“SpringCloudGateway--过滤器(内置filter)”的评论:

还没有评论