springcloud gateway自定义过滤器
- 背景描述
- 实现
-
- 声明路由规则
- 自定义拦截器
- 总结
- 其他
背景描述
关于springcloud gateway的简单用法和原理,网上文章很多,也可以查看spring官网。
这里有一个需求,根据请求体里的某个参数值来判断,该路由去哪个服务。请求体数据是加密的,需要先解密。gateway和nacos配合使用。
版本:
- springboot:2.3.8.RELEASE
- nacos:1.4.1
实现
请求参数:{type: “1”},根据type值来动态决定路由去向
声明路由规则
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder){
return builder.routes().route("decode_data",predicateSpec ->
//参考官网https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the-path-route-predicate-factory
predicateSpec.path("/demo/{segment}/**")
//modifyRequestBody可以修改请求体
//参考官网https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#modify-a-request-body-gatewayfilter-factory
.filters(f-> f.modifyRequestBody(
//RequestData是自定义的一个对象,接收请求体参数
RequestData.class,JSONObject.class,MediaType.APPLICATION_JSON_VALUE,
(exchange,data)->{
//原始请求体参数是加密的,这里可以解密,data是原始请求体数据
//...
//newData是转换后得到的请求体数据
JSONObject newData = new JSONObject();
//exchange.getAttributes()里面可以存放一些缓存数据,给后续的过滤器使用
exchange.getAttributes().put("自定义attrName",data.getType());
return Mono.just(newData);
})
//这里可以用lambda表达式,个人喜好
.filter(productHandlerGatewayFilterFactory.apply(c->{})))
//随便写一个符合规则的url地址,如果不符合规则会报错。最终会被自定义的过滤器替换掉
.uri("http://www.baidu.com"))
.build();
}
自定义拦截器
拦截器类的类名后缀有固定值:自定义+GatewayFilterFactory
ProductHandlerGatewayFilterFactory
@Component
@Slf4j
//排序值越小越靠前,可为负数
@Order(10)
public class ProductHandlerGatewayFilterFactory
extends AbstractGatewayFilterFactory<ProductHandlerGatewayFilterFactory.Config>{
public ProductHandlerGatewayFilterFactory(){
super(Config.class);
}
@Override
public GatewayFilter apply(Config config){
return ((exchange,chain)->{
ServerHttpRequest req = exchange.getRequest();
//前面路由规则中断言Path使用了{segment}变量,可以用下面的方式获取到
Map<String, String> uriVariables = ServerWebExchangeUtils.getUriTemplateVariables(exchange);
String segment = uriVariables.get("segment");
//获取到当前的路由规则
Route oldRoute = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
String newPath = "";
String type = exchange.getAttribute("自定义attrName");
//业务处理逻辑,根据type值动态改变route uri
if(type.equals("1")){
//结束过滤链
return exchange.getResponse().setComplete();
}else{
//...业务逻辑处理,给newPath赋值
}
//给request和exchange赋值新的route对象
URI newUri = URI.create(newPath);
ServerHttpRequest request = req.mutate().uri(newUri).build();
Route newRoute = Route.async()
.asyncPredicate(oldRoute.getPredicate())
.filters(oldRoute.getFilters())
.id(oldRoute.getId())
.order(oldRoute.getOrder())
.uri(newUri)
.build();
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR,newRoute);
return chain.filter(exchange.mutate().request(request).build());
});
}
@Validated
public static class Config {
//可以通过yml配置文件来传入一些自定义的参数
}
}
总结
先根据断言规则匹配
Path("/demo/{segment}/**")
,然后使用
ModifyRequestBodyGatewayFilterFactory
过滤器,把请求体参数解密,解密后的数据重新放进后续的过滤链中,然后自定义一个过滤器,从请求体参数中判断该走哪个路由。
代码中使用了一些不好理解的代码段,这些都是从gateway自带有的过滤器源码中学习到的,如果直接讲源码,枯燥难懂。spring官方文档写的很粗略,gateway的高端用法还是要阅读源码来学习到。
其他
- 打开gateway日志输出:
日志级别改为debug,可以看到gateway自带有的过滤器的order顺序(值越小排越前面,可为负数)org.springframework.cloud.gateway
- gateway的yml配置可以放入nacos中,这样gateway就不用重启,修改nacos配置文件配合自定义的一些过滤器处理,即可实现动态变化gateway的路由。当然也可以配合mysql或redis进行动态路由,网上有很多相关文章供参考。