springcloud gateway高级功能之根据参数自定义路由Predicate

背景

我们使用了springcloud gateway作为也给路由转发功能,由于历史遗留问题,不仅仅需要根据path转发,还需要根据get或者post中的参数进行转发

解决方案

这里我们使用自定义的Predicate进行转发

简介

这里简单介绍下相关术语 (1)Filter(过滤器):

和Zuul的过滤器在概念上类似,可以使用它拦截和修改请求,并且对上游的响应,进行二次处理。过滤器为org.springframework.cloud.gateway.filter.GatewayFilter类的实例。

(2)Route(路由):

网关配置的基本组成模块,和Zuul的路由配置模块类似。一个Route模块由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配,目标URI会被访问。

(3)Predicate(断言):

这是一个 Java 8 的 Predicate,可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或参数。断言的输入类型是一个 ServerWebExchange。

这里我们会使用自定义的断言来实现,常用的断言有如下几个:

在这里插入图片描述

详细信息可以参考下面链接:https://www.jianshu.com/p/d2c3b6851e1d?utm_source=desktop&utm_medium=timeline

GET请求转发

在常用断言中就有支持根据get参数转发,所以这里需要同时使用path以及query断言,可以根据如下配置

1spring: 2 cloud: 3 gateway: 4 routes: 5 - id: blog 6 uri: http://blog.yuqiyu.com 7 predicates: 8 - Path=/api/demo 9 - Query=xxx, zzz

根据上面配置,我们限定了参数xxx必须为zzz时才会被成功转发,否则会出现404抓发失败,根据上面配置就可以根据get参数转发

POST请求转发

post参数转发,没有现成的转发断言,这里我们需要参考readbody断言来实现,下面是ReadBodyPredicateFactory 的源码

1public class ReadBodyPredicateFactory extends AbstractRoutePredicateFactory<ReadBodyPredicateFactory.Config> { 2 protected static final Log log = LogFactory.getLog(ReadBodyPredicateFactory.class); 3 private static final String TEST_ATTRIBUTE = "read_body_predicate_test_attribute"; 4 private static final String CACHE_REQUEST_BODY_OBJECT_KEY = "cachedRequestBodyObject"; 5 private static final List<HttpMessageReader<?>> messageReaders = HandlerStrategies.withDefaults().messageReaders(); 6 7 public ReadBodyPredicateFactory() { 8 super(ReadBodyPredicateFactory.Config.class); 9 } 10 11 public AsyncPredicate<ServerWebExchange> applyAsync(ReadBodyPredicateFactory.Config config) { 12 return (exchange) -> { 13 Class inClass = config.getInClass(); 14 Object cachedBody = exchange.getAttribute("cachedRequestBodyObject"); 15 if (cachedBody != null) { 16 try { 17 boolean test = config.predicate.test(cachedBody); 18 exchange.getAttributes().put("read_body_predicate_test_attribute", test); 19 return Mono.just(test); 20 } catch (ClassCastException var6) { 21 if (log.isDebugEnabled()) { 22 log.debug("Predicate test failed because class in predicate does not match the cached body object", var6); 23 } 24 25 return Mono.just(false); 26 } 27 } else { 28 return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange, (serverHttpRequest) -> { 29 return ServerRequest.create(exchange.mutate().request(serverHttpRequest).build(), messageReaders).bodyToMono(inClass).doOnNext((objectValue) -> { 30 exchange.getAttributes().put("cachedRequestBodyObject", objectValue); 31 }).map((objectValue) -> { 32 return config.getPredicate().test(objectValue); 33 }); 34 }); 35 } 36 }; 37 } 38 39 public Predicate<ServerWebExchange> apply(ReadBodyPredicateFactory.Config config) { 40 throw new UnsupportedOperationException("ReadBodyPredicateFactory is only async."); 41 } 42 43 public static class Config { 44 private Class inClass; 45 private Predicate predicate; 46 private Map<String, Object> hints; 47 48 public Config() { 49 } 50 51 public Class getInClass() { 52 return this.inClass; 53 } 54 55 public ReadBodyPredicateFactory.Config setInClass(Class inClass) { 56 this.inClass = inClass; 57 return this; 58 } 59 60 public Predicate getPredicate() { 61 return this.predicate; 62 } 63 64 public ReadBodyPredicateFactory.Config setPredicate(Predicate predicate) { 65 this.predicate = predicate; 66 return this; 67 } 68 69 public <T> ReadBodyPredicateFactory.Config setPredicate(Class<T> inClass, Predicate<T> predicate) { 70 this.setInClass(inClass); 71 this.predicate = predicate; 72 return this; 73 } 74 75 public Map<String, Object> getHints() { 76 return this.hints; 77 } 78 79 public ReadBodyPredicateFactory.Config setHints(Map<String, Object> hints) { 80 this.hints = hints; 81 return this; 82 } 83 } 84}

这个只是把post参数读入到缓存,配置如下

1predicates: 2 - Path=/card/api/** 3 - name: ReadBodyPredicateFactory #使用ReadBodyPredicateFactory断言,将body读入缓存 4 args: 5 inClass: '#{T(String)}' 6 predicate: '#{@bodyPredicate}' #注入实现predicate接口类

但是这个暂时不能满足要求,我们需要参考ReadBodyPredicateFactory自定义一个predicatefactory来实现我们的需求

1@Component() 2@Slf4j 3public class MyReadBodyPredicateFactory extends AbstractRoutePredicateFactory<MyReadBodyPredicateFactory.Config> { 4 5 6 private static final List<HttpMessageReader<?>> messageReaders = HandlerStrategies 7 .withDefaults().messageReaders(); 8 9 public MyReadBodyPredicateFactory() { 10 super(MyReadBodyPredicateFactory.Config.class); 11 } 12 13 public MyReadBodyPredicateFactory(Class<MyReadBodyPredicateFactory.Config> configClass) { 14 super(configClass); 15 } 16 17 @Override 18 @SuppressWarnings("unchecked") 19 public AsyncPredicate<ServerWebExchange> applyAsync(MyReadBodyPredicateFactory.Config config) { 20 return new AsyncPredicate<ServerWebExchange>() { 21 @Override 22 public Publisher<Boolean> apply(ServerWebExchange exchange) { 23 Object cachedBody = exchange.getAttribute(MyFacadeConstants.CACHE_REQUEST_BODY_OBJECT_KEY); 24 if (cachedBody != null) { 25 try { 26 boolean test = match(config.sceneIds, (MycRequest) cachedBody); 27 return Mono.just(test); 28 } catch (ClassCastException e) { 29 if (log.isDebugEnabled()) { 30 log.debug("Predicate test failed because class in predicate " 31 + "does not match the cached body object", e); 32 } 33 } 34 return Mono.just(false); 35 } else { 36 return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange, 37 (serverHttpRequest) -> ServerRequest.create(exchange.mutate().request(serverHttpRequest).build(), messageReaders) 38 .bodyToMono(MycRequest.class) 39 .doOnNext(objectValue -> exchange.getAttributes().put(MyFacadeConstants.CACHE_REQUEST_BODY_OBJECT_KEY,objectValue)) 40 .map(objectValue -> { return match(config.sceneIds, objectValue);})); 41 } 42 } 43 }; 44 } 45 46 private boolean match(String params, MycRequest mycRequest) { 47 if("others".equals(params)){ 48 return true; 49 } 50 String[] paramArray = params.split(","); 51 if (ArrayUtils.contains(paramArray, mycRequest.getRouteId)) { 52 return true; 53 } else { 54 return false; 55 } 56 } 57 58 @Override 59 @SuppressWarnings("unchecked") 60 public Predicate<ServerWebExchange> apply(MyReadBodyPredicateFactory.Config config) { 61 throw new UnsupportedOperationException( 62 "MyReadBodyPredicateFactory is only async."); 63 } 64 65 public static class Config { 66 67 private String params; 68 69 public MyReadBodyPredicateFactory.Config setParams(params) { 70 this.params = params; 71 return this; 72 } 73 74 public String getParams() { 75 return params; 76 } 77 } 78}

这里我们可以根据将参数转为MyRequest,然后再进行判断是否路由,当然这里我们同样也需要使用到path断言,配置如下:

1spring: 2 cloud: 3 gateway: 4 routes: 5 - id: route1 6 uri: http://host1:8080 7 predicates: 8 - Path=/api/demo 9 - name: MyReadBodyPredicateFactory 10 args: 11 params: "23,22" 12 - id: route2 13 uri: http://host2:8080 14 predicates: 15 - Path=/api/demo 16 - name: RecommendReadBodyPredicateFactory 17 args: 18 params: "44,56"

这样就可以根据post参数路由转发了,如下监控:

在这里插入图片描述

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )