笔者使用spring cloud gateway时踩了个很多人都遇到的坑,这个也是spring boot和spring cloud gateway的开发者们没有协商好导致的问题(后面会说)。
环境
JDK1.8
spring boot 2.0.6.RELEASE
问题表现
- 使用POST请求gateway时,后端微服务拿不到body信息,仿佛就是gateway吞掉了body(其实就是);
- gateway开DEBUG模式,查看报如下错误:
1 2 3 4 5 6 7 8 9
java.lang.IllegalStateException: Only one connection receive subscriber allowed. at reactor.ipc.netty.channel.FluxReceive.startReceiver(FluxReceive.java:279) at reactor.ipc.netty.channel.FluxReceive.lambda$subscribe$2(FluxReceive.java:129) at io.netty.util.concurrent.AbstractEventExecutor.safeExecute$$$capture(AbstractEventExecutor.java:163) at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java) at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:446) at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:884) at java.lang.Thread.run(Thread.java:745)
大意就是netty的request body只能读取一次,第二次读取就报这个错误了。
问题原因
翻查GitHub终于找到,spring boot在2.0.5版本如果使用了WebFlux就自动配置HiddenHttpMethodFilter
过滤器。
查看源码发现,这个过滤器的作用是,针对当前的浏览器一般只支持GET
和POST
表单提交方法,如果想使用其他HTTP方法(如:PUT、DELETE、PATCH
),就只能通过一个隐藏的属性如(_method=PUT
)来表示,那么HiddenHttpMethodFilter
的作用是将POST
请求的_method
参数里面的value替换掉http请求的方法。
想法是很好的,用一种折中的方法来支持使浏览器支持restful方法。
如果只是使用spring boot,一切都是没有问题的,因为使用的过程中,不需要我们自己解析request body,到controller这一层,这一切就已经完成的了。
但是spring cloud gateway需要,因为它的做法就是拿到原始请求信息(包括request body),再重新封装一个request路由到下游微服务,所以上面的问题就在于:
-
HiddenHttpMethodFilter
读取了一次request body; -
gateway的封装自己的request时,去读取request body,就报错了。
所以这个是spring cloud gateway和spring boot开发者没协商好,都去读取request body的问题。
问题解决方案
HiddenHttpMethodFilter
是spring boot在2.0.5版本自动引入的,将版本降到2.0.4即可。- 如果不降版本,也可以自己重写
HiddenHttpMethodFilter
来覆盖原来的实现,如下:1 2 3 4 5 6 7 8 9
@Bean public HiddenHttpMethodFilter hiddenHttpMethodFilter() { return new HiddenHttpMethodFilter() { @Override public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { return chain.filter(exchange); } }; }
filter方法什么都不做,自然就不会读取request body了,你可能会想,那不就不支持HiddenHttpMethodFilter
的功能了吗,其实gateway本身就不应该做这种事情,原始请求是怎样的,转发给下游的请求就应该是怎样的。下游服务如果使用的是也是spring boot的服务,那么下游服务自己会做HiddenHttpMethodFilter
的功能。
这也是gateway官方开发者目前所提出的解决方案。
更多信息可以查看这个issues
spring boot的2.0.5版本添加了HiddenHttpMethodFilter
内容,入口
如果想查看其他spring cloud gateway的案例和使用,可以点击查看
有什么问题,或者有更好的解决方案,也可以在下方留言。
- 本文作者:二当家的
- 本文链接: 2019/01/08/spring-cloud-gateway的POST请求问题解决:Only-one-connection-receive-subscriber-allowed/
- 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 CN 许可协议。转载请注明出处!
- 彩蛋: 左边Overview微信公众号二维码,扫描它获取更多技术信息