天天看点

springcloud gateway自定义过滤器背景描述实现总结其他

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日志输出:

    org.springframework.cloud.gateway

    日志级别改为debug,可以看到gateway自带有的过滤器的order顺序(值越小排越前面,可为负数)
  • gateway的yml配置可以放入nacos中,这样gateway就不用重启,修改nacos配置文件配合自定义的一些过滤器处理,即可实现动态变化gateway的路由。当然也可以配合mysql或redis进行动态路由,网上有很多相关文章供参考。