springcloud gateway代理get正常、post请求报错的问题

时间:2024-03-13 19:25:44

在上一篇时,我们在使用gateway的反向代理功能时,发现了一个很严重的问题,那就是通过gateway去访问后端服务时,如果发起的是Get请求,就一切正常,如果是Post请求,就会报错。无论是使用什么filter。

java.lang.IllegalStateException: Only one connection receive subscriber allowed.
	at reactor.ipc.netty.channel.FluxReceive.startReceiver(FluxReceive.java:279) [reactor-netty-0.7.10.RELEASE.jar:0.7.10.RELEASE]
	at reactor.ipc.netty.channel.FluxReceive.lambda$subscribe$2(FluxReceive.java:129) [reactor-netty-0.7.10.RELEASE.jar:0.7.10.RELEASE]
	at reactor.ipc.netty.channel.FluxReceive$$Lambda$714/21949105.run(Unknown Source) [reactor-netty-0.7.10.RELEASE.jar:0.7.10.RELEASE]
	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163) [netty-common-4.1.29.Final.jar:4.1.29.Final]
	at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404) [netty-common-4.1.29.Final.jar:4.1.29.Final]
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:446) [netty-transport-4.1.29.Final.jar:4.1.29.Final]
	at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:884) [netty-common-4.1.29.Final.jar:4.1.29.Final]
	at java.lang.Thread.run(Thread.java:745) [na:1.8.0_40]

如果使用的是Springboot2.0.5之前的版本,不存在该问题,之后的但凡是非Get请求,就会报该错误。

该异常意思是请求体只能被消费一次,也就是说这个请求的body已经被读取过一次了,再次封装转发时会报错。这个问题比较怪异,很早之前我们知道@RequestBody接收的参数,是不能被读取第二次的,假如被网关的日志读取消费过了,那么后续的服务就无法再接收到该参数了。要想保持参数还在,就只能自己再去构造一个同样的RequestBody,发给后端的服务。但是,这次是普通的Post的form表单,居然也报这个错,就比较奇怪了。

gateway反向代理的原理是,首先读取原请求的数据,然后构造一个新的请求,将原请求的数据封装到新的请求中,然后再转发出去。

该错误是Springboot的升级导致的,具体的详细解析在https://github.com/spring-cloud/spring-cloud-gateway/issues/541

最终作者也给了解决方案

springcloud gateway代理get正常、post请求报错的问题

 @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
        return new HiddenHttpMethodFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
                return chain.filter(exchange);
            }
        };
    }

加上上面这一段就OK了。

实测,确实OK。