目录
一、概览
二、内置过滤器
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博客
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 {
public static final String PARTS_KEY = "parts";
public StripPrefixGatewayFilterFactory() {
super(StripPrefixGatewayFilterFactory.Config.class);
}
public List shortcutFieldOrder() {
return Arrays.asList("parts");
}
public GatewayFilter apply(StripPrefixGatewayFilterFactory.Config config) {
return new GatewayFilter() {
public Mono 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;
}
}
}
添加请求头参数,参数和值之间使用逗号分隔
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 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();
}
};
}
}
在响应的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 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();
}
};
}
}
对指定响应头去重复,也即某个响应头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 {
private static final String STRATEGY_KEY = "strategy";
public DedupeResponseHeaderGatewayFilterFactory() {
super(DedupeResponseHeaderGatewayFilterFactory.Config.class);
}
public List shortcutFieldOrder() {
return Arrays.asList("name", "strategy");
}
public GatewayFilter apply(DedupeResponseHeaderGatewayFilterFactory.Config config) {
return new GatewayFilter() {
public Mono 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 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() {
}
}
}
添加请求参数
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
实现熔断时使用,支持CircuitBreaker和Hystrix两种,这个功能会单开一篇文章阐述。
添加降级时的异常信息,一般与CircuitBreaker配合使用。
限流,会单开文章阐述。地址:SpringCloudGateway--基于redis实现令牌桶算法_雨欲语的博客-CSDN博客
重定向,连个参数,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
删除请求头
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
删除响应头
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
删除请求参数
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
重写请求路径,比如我们之前都是访问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/?(?.*), /nacos/$\{segment}
metadata:
connect-timeout: 15000 #ms
response-timeout: 15000 #ms
修改响应的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
如果项目中使用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
在响应的头中添加很多安全相关的信息:
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
功能和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
设置请求头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
设置响应的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
设置返回的code:
routes:
- id: service-one
uri: lb://service-one
predicates:
- Path=/service/**
filters:
- SetStatus=500
metadata:
connect-timeout: 15000 #ms
response-timeout: 15000 #ms
给请求路径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
设置重试次数:
①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
控制请求大小,单位有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
修改请求体body内容,官方推荐使用代码完成。
修改响应body的内容,也是推荐使用代码完成。
用于键值对的赋值,以下意思是如果请求的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一样。
在转发请求到服务提供者时,保留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
用于改写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
修改请求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