一、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
,即保留第一个头
- RETAIN_FIRST:保留第一个值
- RETAIN_LAST:保留最后一个值
- 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
参数中。
- fromHeader 在 header 中不存在,那么没有什么影响。
- fromHeader 存在
toHeader 存在,那么往配置中 toHeader 对应的 header 中追加 fromHeader对应的值
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} # 当访问/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、需求:
修改原始服务的参数,增加username
和roles
参数传递到下游服务。
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 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();
}
}
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
来排序。
GlobalFilter
有pre
和post
2个执行阶段,优先级越高 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/**
设置
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
五、自定义全局过滤器
- 实现 GlobalFilter 接口和 Ordered 接口
- 加入到 Spring 管理
- 代码实现
@Slf4j
@Component
public class CustomGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono 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
类
2、继承AbstractGatewayFilterFactory
类时,构造方法中需要调用 super(C.class)
3、重写 shortcutFieldOrder
方法,定义配置文件中字段的位置
4、编写的过滤器类必须要以 GatewayFilterFactory
结尾
5、自定义过滤器的名字为,GatewayFilterFactory
前面的字符,比如 TokenGatewayFilterFactory,那么过滤器名字是 Token
5、功能实现:过滤器实现,从请求中获取一个参数的值,打印出来
1、Java代码实现
@Component
@Slf4j
public class TokenGatewayFilterFactory extends AbstractGatewayFilterFactory {
public TokenGatewayFilterFactory() {
super(Config.class);
}
@Override
public List 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 metadata = route.getMetadata();
log.info("routeId:[" + routeId + "]" + "metadata:[" + metadata + "]");
2、过滤器的执行顺序
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 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