前言

最近在开发api-gateway的时候遇到了一个问题,网上能够找到的解决方案也很少,之后由公司的大佬解决了这个问题。写下这篇文章记录一下解决方案。希望可以帮助到更多的人。

环境

  • java版本:8
  • 框架:spring-cloud 2.0.0.RC1

介绍

api-gateway主要接收前端请求,然后对请求的数据进行验证,验证之后请求反向代理到服务器。当请求 method 为 GET 时,可以顺利通过api-gateway。当请求 method 为 POST 时,api-gateway则会报如下错误:

2018-07-18 11:49:04.073 ERROR 3025 --- [ctor-http-nio-4] .a.w.r.e.DefaultErrorWebExceptionHandler : Failed to handle request [POST http://localhost:9000/api/hello]

java.lang.IllegalStateException: Only one connection receive subscriber allowed.
at reactor.ipc.netty.channel.FluxReceive.startReceiver(FluxReceive.java:276) ~[reactor-netty-0.7.5.RELEASE.jar:0.7.5.RELEASE]
at reactor.ipc.netty.channel.FluxReceive.lambda$subscribe$2(FluxReceive.java:127) ~[reactor-netty-0.7.5.RELEASE.jar:0.7.5.RELEASE]
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163) ~[netty-common-4.1.22.Final.jar:4.1.22.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404) ~[netty-common-4.1.22.Final.jar:4.1.22.Final]
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:463) ~[netty-transport-4.1.22.Final.jar:4.1.22.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:886) ~[netty-common-4.1.22.Final.jar:4.1.22.Final]
at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_171]

错误分析

实际上spring-cloud-gateway反向代理的原理是,首先读取原请求的数据,然后构造一个新的请求,将原请求的数据封装到新的请求中,然后再转发出去。然而我们在他封装之前读取了一次request body,而request body只能读取一次。因此就出现了上面的错误。

解决方案

对于上面的错误我们给出的解决方案是:

读取request body的时候,我们再封装一次request,转发出去

下面是我们的代码:

@Component
public class PostFilter extends AbstractNameValueGatewayFilterFactory { private static final String X_APP_ID_HEADER = "X-app-id";
public static final String X_FORWARDED_FOR = "X-Forwarded-For"; @Override
public GatewayFilter apply(NameValueConfig nameValueConfig) {
return (exchange, chain) -> {
URI uri = exchange.getRequest().getURI();
URI ex = UriComponentsBuilder.fromUri(uri).build(true).toUri();
ServerHttpRequest request = exchange.getRequest().mutate().uri(ex).build();
if("POST".equalsIgnoreCase(request.getMethodValue())){//判断是否为POST请求
Flux<DataBuffer> body = request.getBody();
AtomicReference<String> bodyRef = new AtomicReference<>();//缓存读取的request body信息
body.subscribe(dataBuffer -> {
CharBuffer charBuffer = StandardCharsets.UTF_8.decode(dataBuffer.asByteBuffer());
DataBufferUtils.release(dataBuffer);
bodyRef.set(charBuffer.toString());
});//读取request body到缓存
String bodyStr = bodyRef.get();//获取request body
System.out.println(bodyStr);//这里是我们需要做的操作
DataBuffer bodyDataBuffer = stringBuffer(bodyStr);
Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer); request = new ServerHttpRequestDecorator(request){
@Override
public Flux<DataBuffer> getBody() {
return bodyFlux;
}
};//封装我们的request
}
return chain.filter(exchange.mutate().request(request).build());
};
} protected DataBuffer stringBuffer(String value) {
byte[] bytes = value.getBytes(StandardCharsets.UTF_8); NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
buffer.write(bytes);
return buffer;
}
}

至此,该问题得到解决。

最新文章

  1. AspNetPager 多条件分页查询
  2. HTML5-02 元素
  3. js获取modelandview的值
  4. 网页中插入FLASH(swf文件)的html代码
  5. 【Java基础】序列化与反序列化深入分析
  6. Android Service 与 IntentService
  7. Lintcode: Segment Tree Build
  8. getDefinitionByName getDefinition 区别
  9. Android用户界面布局(layouts)
  10. 下面css hack的写法分别用于哪些浏览器
  11. PHP常用字符串函数
  12. hdu 1282回文数猜想
  13. C 文件直接包含
  14. POJ 1915-Knight Moves (单向BFS &amp;amp;&amp;amp; 双向BFS 比)
  15. css ::before和::after伪元素的用法
  16. Java实现读取文章中重复出现的中文字符串
  17. cocoa编程第4版 8.5 挑战1 解答
  18. 用python写一个非常简单的QQ轰炸机
  19. facebook api之Ads Insights API
  20. 【BZOJ5188】 [Usaco2018 Jan]MooTube

热门文章

  1. [转载]GridView中点击某行的任意位置就选中该行
  2. c#获取桌面路径和bin文件的路径
  3. warning C4819 的解决方法
  4. opencv+ linux + cmake 生成 opencv静态库
  5. springboot 自动装配
  6. Spring讲解-----------表达式语言
  7. 5、SVN 权限管理
  8. VMwarevSphere Client 链接 vCenter Server中的主机,开启虚拟机提示:在主机当前连接状况下不允许执行该操作
  9. C++ 批量打开写入文件
  10. GridView 二维排布