Gateway的路由谓词工厂
文章目录
相关概念(术语)
- 路由 (Route): 路由是网关的基础构建模块,它是有ID,目标URI,谓词集合和过滤器集合定义。如果聚合谓词为true,则匹配路由。
- 谓词 (Predicate): 这是Java8的函数谓词,输入类型是Spring Framework
ServerWebExchange
, 这使得开发者可以匹配HTTP请求中的所有内容,例如 header 和 参数。 - 过滤器 (Filter): 这些是使用特定工厂构建的SpringFramework
GatewayFilter
实例。在此,可以在发送下游请求之前或之后修改请求和响应。
路由谓词工厂
SpringCloudGateway 将路由作为 Spring WebFlux HandlerMapping
基础架构的一部分进行匹配。SpringCloudGateway 包括许多内置的Route Predicate factory。所有这些谓词都与HTTP请求的不同属性匹配。多个Route Predicate工厂可以合并,也可以通过逻辑合并 and
。
1. After 路由谓词
After Route Predicate Factory 采用一个参数,即日期时间。该谓词匹配在当前日期时间之后发生的请求。
2. Before 路由谓词
Before Route Predicate Factory 采用一个参数,即日期时间。该谓词匹配在当前日期时间之前发生的请求。
3. Between 路由谓词
Between Route Predicate Factory 采用两个参数,都是日期时间类型的,该谓词匹配在两个日期之间发生的请求。
下面是统一的配置:
spring:
cloud:
gateway:
routes:
- id: gateway-service
uri: https://www.163.com
predicates:
# - Before= 2019-10-25T00:00:00+08:00[Asia/Shanghai]
# - After= 2019-10-25T00:00:00+08:00[Asia/Shanghai]
- Between= 2019-10-25T00:00:00+08:00[Asia/Shanghai],2019-10-25T18:05:00+08:00[Asia/Shanghai]
说明: 如果是时间之前的,使用Before
关键字。如果是时间之后的,使用After
关键字,如果要指明在两个日期之间,则使用Between
关键字,两个日期使用逗号隔开。[Asia/Shanghai]
表示使用的是以上海
为基准的时间。
4. Cookie 路由谓词
Cookie路由谓词工厂采用的是两个参数,一个是Cookie的名字,另外一个是合法的正则表达式。这个谓词用于匹配具有给定名称的Cookie,并且值匹配一个合法的正则表达式。
spring:
cloud:
gateway:
routes:
- id: gateway-service
uri: http://www.163.com
predicates:
- Cookie= username,kee.e
说明:这里是采用验证cookie的方式,当我们请求中携带了对应的正确的cookie信息,就可以访问成功到http://www.163.com
, 否则就会报 Not Found
.
下面是使用Postman测试的结果:
错误的cookie信息:
正确的cookie信息:
如果使用gateway时, 需要取出 cookie信息,可以使用下面的方法:
@GetMapping("/resp")
public String test(@CookieValue("username") String username) {
return "SUCCESS";
}
5. Header 路由谓词
Header 路由谓词工厂接受两个参数:header的name和其对应的合法的正则表达式值。只有在Header部分存在name并且其value也匹配的情况下,才能通过。
spring:
cloud:
gateway:
routes:
- id: gateway-service
uri: http://www.163.com
predicates:
- Header=password,123456
测试结果如下:
如果使用Gateway,需要从Header中取出信息,可以使用下面的方法:
@GetMapping(value = "/testHeader")
public String testHeader(@RequestHeader(value = "username") String username){
return "username:"+username;
}
6. Host 路由谓词
Host(主机)路由谓词工厂,采用一个参数:主机名模式列表。该模式带有.
作为分隔符的Ant样式的模式,谓词与Host
匹配模式的Header部分。
spring:
cloud:
gateway:
routes:
- id: gateway-service
uri: http://localhost:8888
predicates:
- Host= localhost:9999
这里为了更好的测试,我们单创建并启动一个服务,端口为8888的,大概代码如下:
/**
* @author hao.ouYang
* @create 2019-10-25 18:08
*/
@RestController
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/user")
public User findUser(){
return userService.findUser();
}
}
@Service
public class UserService {
public User findUser(){
User user = new User(1,"admin","admin");
return user;
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
private Integer id;
private String username;
private String password;
}
测试结果如下:
上面其实是将http://localhost:9999/user
映射到了 http://localhost:8888/user
上了。
注意:当然除了我们除了上面的写法,还有一些多样化的定制。同时也支持URI模板变量
- Host= www.ouyang.**, **.ouyang.**,**.ouyang.org
# 上面的例子可以匹配 www.ouyang.club, www.ouyang.com, www.ouyangorg等。
- Host= {sub}.ouyang.club
7. Method 路由谓词
方法路由谓词工厂采用一个参数:用来匹配HTTP请求的类型。(GET,POST等).
spring:
cloud:
gateway:
routes:
- id: gateway-service
uri: http://localhost:8888
predicates:
- Method= POST
以上需要匹配为POST
类型的请求才可以转发到 http://localhost:8888
上面,测试如下:
更改为POST
类型的请求:
8. Path 路由谓词
PathRoutePredicateFactory
需要PathMatcher
模式路径列表和一个可选的标志位参数matchOptionalTrailingSeparator
. 这是最常用的一个路由谓词。
spring:
cloud:
gateway:
routes:
- id: gateway-service
uri: http://localhost:8888
predicates:
- Path= /path
这种使用路径来映射的方式。以上可以将http://localhost:9999/path
映射到http://localhost:8888/path
路径上,前一节也见过这个谓词,所以说这个谓词是最常用。还可以通过下面这个方式在请求路径上携带参数:(如果通过这种形式配置,在匹配命中进行路由的同时,会提取路径中对应的内容并且将键值对放在ServerWebExchange.getAttributes()
集合中,Key为ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE
, 这些提取出来的属性可以供GatewayFilter Factories
使用)。
spring:
cloud:
gateway:
routes:
- id: gateway-service
uri: http://localhost:8888
predicates:
- Path= /testPath/{stri}
另一个服务的接收:
@GetMapping(value = "/path")
public String path(){
return "path";
}
@GetMapping(value = "/testPath/{str}")
public String testPath(@PathVariable("str") String str){
return "return:"+str;
}
9. Query 路由谓词
请求查询参数路由工厂QueryRoutePredicateFactory
需要一个必须的请求查询参数(Param的name)以及一个可选的正则表达式(regexp).
spring:
application:
name: spring-cloud-gateway
cloud:
gateway:
routes:
- id: gateway-service
uri: http://localhost:8888
predicates:
- Query= username
通过上述配置,我们的请求中只需要包含username
参数即可匹配路由。
curl localhost:9999/testQuery?username=1
上面的测试可以通过,但是如果把username
删除或是改成别的,就无法匹配了。
还可以将Query参数以键值对的形式来配置,这样的请求过来的时候,不仅需要匹配名字,同时参数值需要与正则表达式匹配才能走路径。
spring:
application:
name: spring-cloud-gateway
cloud:
gateway:
routes:
- id: gateway-service
uri: http://localhost:8888
predicates:
- Query= username,ad.
以上匹配需要请求满足既有username
参数,并且值需要以ad
开头的长度为3的字符串才能匹配和路由。
测试如下:
curl http://localhost:9999/testQuery?username=adv
{"param":"adv"}
curl http://localhost:9999/testQuery?username=addv
{"timestamp":"2019-10-27T09:15:45.580+0000","path":"/testQuery","status":404,"error":"Not Found","message":null}
8888端口服务的controller方法为:
@GetMapping(value = "/testQuery")
public Map<String,String> testQuery(@RequestParam("username") String username){
Map<String,String> map = new HashMap<>();
map.put("param",username);
return map;
}
10. RemoteAddr 路由谓词
RemoteAddrRoutePredicateFactory
匹配规则采用CIDR符号(IPv4或IPv6)字符串的列表(最小值为1),例如192.168.0.1/16(其中192.168.0.1是远程IP地址并且16是子网掩码)。
spring:
application:
name: spring-cloud-gateway
cloud:
gateway:
routes:
- id: gateway-service
uri: http://localhost:8888
predicates:
- RemoteAddr= 127.0.0.1
server:
port: 9999
8888端口服务的处理方法:
@GetMapping(value = "/remote")
public String testRemote(){
return "Remote";
}
测试结果:
curl http://127.0.0.1:9999/remote
# 响应结果
Remote
11. Weight 路由谓词
权重路由谓词工厂采用两个参数,分别为组(group)和权重(weight). 权重按组进行计算。
spring:
application:
name: spring-cloud-gateway
cloud:
gateway:
routes:
- id: gateway-weight-high
uri: http://localhost:8888
predicates:
- Weight= Group1, 8
- id: gateway-weight-low
uri: http://localhost:7777
predicates:
- Weight= Group1, 2
server:
port: 9999
接受7777和8888请求的方法:
//============port:7777 ============
@GetMapping(value = "/weight")
public Map weight() {
Map<String, Object> map = new HashMap<>();
map.put("port", "7777");
return map;
}
//============port:8888 ============
@GetMapping(value = "/weight")
public Map weight() {
Map<String, Object> map = new HashMap<>();
map.put("port", "8888");
System.out.println(map);
return map;
}
测试结果如下:
参考了: https://www.cnblogs.com/throwable/p/10807704.html