clazz.getSimpleName()
.replace(GatewayFilterFactory.class.getSimpleName(), "")
getSimpleName()是jdk提供得到类的简写名称的方法。可以先理解为就是获得类名的方法,比如:
public static void main(String[] args) {
String simpleName = KeyPairGatewayFilterFactory.class.getSimpleName();
System.out.println(simpleName);
}
这里介绍几种经典的内置过滤器:
通过名称我们可以快速的明白这个过滤器工厂的作用,就是添加请求头。
使用示列:
server:
port: 8081
spring:
profiles:
active: add_request_header_route
---
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: http://httpbin.org:80/get
filters:
- AddRequestHeader=X-Request-Foo, Bar
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
profiles: add_request_header_route
public class AddRequestHeaderGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
@Override
public GatewayFilter apply(NameValueConfig config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest().mutate()
.header(config.getName(), config.getValue())
.build();
return chain.filter(exchange.mutate().request(request).build());
};
}
}
curl localhost:8081
最终显示了从 http://httpbin.org:80/get得到了请求,响应如下:
{
"args": {},
"headers": {
"Accept": "*/*",
"Connection": "close",
"Forwarded": "proto=http;host=\"localhost:8081\";for=\"0:0:0:0:0:0:0:1:56248\"",
"Host": "httpbin.org",
"User-Agent": "curl/7.58.0",
"X-Forwarded-Host": "localhost:8081",
"X-Request-Foo": "Bar"
},
"origin": "0:0:0:0:0:0:0:1, 210.22.21.66",
"url": "http://localhost:8081/get"
}
可以上面的响应可知,确实在请求头中加入了X-Request-Foo这样的一个请求头,在配置文件中配置的AddRequestHeader过滤器工厂生效。
filters:
- AddRequestHeader=NAME, yinjihuan
- AddRequestHeader=NAME2, yinjihuan2
在Nginx服务启中有一个非常强大的功能就是重写路径,Spring Cloud Gateway默认也提供了这样的功能,这个功能是Zuul没有的。在配置文件中加上以下的配置:
spring:
cloud:
gateway:
routes:
- id: rewritepath_route
uri: https://example.org
predicates:
- Path=/red/**
filters:
- RewritePath=/red(?>/?.*), $\{segment}
将匹配的子字符串捕获到一个组名称或编号名称中,在获得匹配结果时,可通过分组名进行获取。例如这里的示例,就是将 “capturing text” 捕获到名称为 “name” 的组中
将名称为name的命名分组所匹配到的文本内容替换到此处
那么就很好解释官网的这个例子了,对于配置文件中的: - RewritePath=/red(?/?.*), ${segment}详解:
(?
1.?
名称为 segment 的组
2./?
/出现0次或1次
3…*
任意字符出现0次或多次
合起来就是:将 /?.*匹配到的结果捕获到名称为segment的组中
${segment}:
将名称为 segment 的分组捕获到的文本置换到此处。
注意,\的出现是由于避免 yaml 语法认为这是一个变量(因为在 yaml 中变量的表示法为 ${variable},而这里我们想表达的是字面含义),在 gateway 进行解析时,会被替换为 ${segment}
最后
业务举例:
将 https://spring.io/projects/** 这个路径重写为 https://spring.io/regexp/**
spring:
cloud:
gateway:
routes:
- id: rewritepath_route
uri: https://spring.io
predicates:
- Path=/projects/**
filters:
- RewritePath=/projects(?>/?.*), /regexp$\{segment}
忽略当前路由的第n段路由,结合part使用
如果配置 StripPrefix=1,则去掉第1层路径
例子: /first/order/info 则变为 /order/info
spring:
application:
name: geteway
cloud:
gateway:
routes:
# lb 前缀匹配 /secnod/102/echo /myprefix/102/echo
- id: 102_lb_forward
uri: lb://order-service # 目标服务地址
predicates:
- Path=/secnod/102/*
filters:
- StripPrefix=1 # 转发之前去掉1层路径 取消 /second
- PrefixPath=/myprefix # 路径前面会加myprefix 加上/myprefix
可以去看下StripPrefixGatewayFilterFactory源码:
与之相似的是PrefixPathGatewayFilterFactory,作用是在当前路由的最前端加上一个自定义路由,例如:
cloud:
gateway:
routes:
- id: devilvan-route-prefix
uri: lb://devilvan-caches/caches
predicates:
- Path=/**
filters:
# PrefixPath:给匹配的Path的前面补充一个前缀,若输入路由:playCaffeineCaches
# 则真正的匹配路由为:/cachesController/playCaffeineCaches
- PrefixPath=/cachesController
那么请求路径就会变为:
http://localhost:9527/playCaffeineCaches == localhost:8077/cachesController/playCaffeineCaches
PrefixPathGatewayFilterFactory源码如下:
写法:
例如之前提到的KeyPairGatewayFilterFactory,记住自定义过滤器工厂类名要求为XXXKeyPairGatewayFilterFactory。
①继承AbstractGatewayFilterFactory(如果需要两个参数就继承AbstractNameValueGatewayFilterFactory),重写apply方法
②需要在启动类或者配置类中将该FilterFactory注册进springIOC
③在配置文件中配置,配置时只需要填写xxxGatewayFilterFactory前面的xxx;
cloud:
gateway:
routes:
- id: devilvan-route-prefix
uri: lb://devilvan-caches/caches
predicates:
- Path=/**
filters:
# PrefixPath:给匹配的Path的前面补充一个前缀,若输入路由:playCaffeineCaches
# 则真正的匹配路由为:/cachesController/playCaffeineCaches
- KeyPair
@Component
public class MyGlobalFilter implements GlobalFilter,Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
boolean token = exchange.getRequest().getHeaders().containsKey("token");
System.out.println("----全局过滤器token----"+token);
if (!token){
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
ServerHttpResponse response = exchange.getResponse();
return response.setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 1;
}
}