Helidon WebClient调用返回302错误,请求排查解决方案
解决Helidon WebClient 302重定向错误问题
问题概述
开发Helidon WebClient时触发如下错误:
Caused by: io.helidon.webclient.WebClientException: Request failed with code 302
实现代码
import java.util.concurrent.ExecutionException; import io.helidon.media.jsonp.JsonpSupport; import io.helidon.webclient.WebClient; import jakarta.enterprise.context.RequestScoped; import jakarta.json.JsonArray; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; @Path("/consumer") @RequestScoped public class ConsumerController { private String producerUrl="http://localhost:8080/books-db/"; @GET public JsonArray getBookData(){ System.out.println("producerUrl :: "+producerUrl); try { WebClient client = WebClient.builder().baseUri(producerUrl) .addMediaSupport(JsonpSupport.create()).addHeader("Accept", "application/json").build(); return client.method("GET").request(JsonArray.class).get(); } catch(InterruptedException | ExecutionException e) { e.printStackTrace(); return null; } } }
完整堆栈跟踪
2023.08.09 12:17:27 INFO io.helidon.common.LogConfig Thread[main,5,main]: Logging at initialization configured using classpath: /logging.properties 2023.08.09 12:17:27 INFO io.helidon.microprofile.server.ServerCdiExtension Thread[main,5,main]: Registering JAX-RS Application: HelidonMP 2023.08.09 12:17:28 INFO io.helidon.webserver.NettyWebServer Thread[nioEventLoopGroup-2-1,10,main]: Channel '@default' started: [id: 0xeba066a3, L:/[0:0:0:0:0:0:0:0]:9090] 2023.08.09 12:17:28 INFO io.helidon.microprofile.server.ServerCdiExtension Thread[main,5,main]: Server started on http://localhost:9090 (and all other host addresses) in 1019 milliseconds (since JVM startup). 2023.08.09 12:17:28 INFO io.helidon.common.HelidonFeatures Thread[features-thread,5,main]: Helidon MP 3.2.2 features: [CDI, Config, Health, JAX-RS, Metrics, Open API, Server] producerUrl :: http://localhost:8080/books-db/ java.util.concurrent.ExecutionException: io.helidon.webclient.WebClientException: Request failed with code 302 at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:396) at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2073) at io.helidon.common.reactive.Single.get(Single.java:730) at com.oracle.controller.ConsumerController.getBookData(ConsumerController.java:25) at com.oracle.controller.ConsumerController$Proxy$_$$_WeldClientProxy.getBookData(Unknown Source) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory.lambda$static$0(ResourceMethodInvocationHandlerFactory.java:52) at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:134) at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:177) at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$TypeOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:219) at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:81) at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:478) at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:400) at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:81) at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:256) at org.glassfish.jersey.internal.Errors$1.call(Errors.java:248) at org.glassfish.jersey.internal.Errors$1.call(Errors.java:244) at org.glassfish.jersey.internal.Errors.process(Errors.java:292) at org.glassfish.jersey.internal.Errors.process(Errors.java:274) at org.glassfish.jersey.internal.Errors.process(Errors.java:244) at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:265) at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:235) at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:684) at io.helidon.webserver.jersey.JerseySupport$JerseyHandler.lambda$doAccept$6(JerseySupport.java:339) at io.helidon.common.context.Contexts.runInContext(Contexts.java:117) at io.helidon.common.context.ContextAwareExecutorImpl.lambda$wrap$7(ContextAwareExecutorImpl.java:154) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) at java.base/java.lang.Thread.run(Thread.java:833) Caused by: io.helidon.webclient.WebClientException: Request failed with code 302 at io.helidon.webclient.WebClientRequestBuilderImpl.getContentFromClientResponse(WebClientRequestBuilderImpl.java:682) at io.helidon.common.reactive.MultiMapperPublisher$MapperSubscriber.onNext(MultiMapperPublisher.java:73) at io.helidon.common.reactive.SingleFromPublisher$SingleSubscriber.onComplete(SingleFromPublisher.java:86) at io.helidon.common.context.Contexts.runInContext(Contexts.java:117) at io.helidon.webclient.WebClientRequestBuilderImpl.runInContext(WebClientRequestBuilderImpl.java:641) at io.helidon.webclient.WebClientRequestBuilderImpl$1.onComplete(WebClientRequestBuilderImpl.java:674) at io.helidon.common.reactive.DeferredScalarSubscription.complete(DeferredScalarSubscription.java:92) at io.helidon.common.reactive.MultiFromCompletionStage$CompletionStageSubscription.accept(MultiFromCompletionStage.java:72) at io.helidon.common.reactive.MultiFromCompletionStage$CompletionStageSubscription.accept(MultiFromCompletionStage.java:52) at io.helidon.common.reactive.MultiFromCompletionStage$AtomicBiConsumer.accept(MultiFromCompletionStage.java:95) at io.helidon.common.reactive.MultiFromCompletionStage$AtomicBiConsumer.accept(MultiFromCompletionStage.java:88) at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863) at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:841) at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510) at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2147) at io.helidon.webclient.NettyClientHandler.lambda$channelRead0$7(NettyClientHandler.java:190) at java.base/java.util.concurrent.CompletableFuture.uniRunNow(CompletableFuture.java:819) at java.base/java.util.concurrent.CompletableFuture.uniRunStage(CompletableFuture.java:803) at java.base/java.util.concurrent.CompletableFuture.thenRun(CompletableFuture.java:2195) at io.helidon.webclient.NettyClientHandler.lambda$channelRead0$9(NettyClientHandler.java:183) at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863) at java.base/java.util.concurrent.CompletableFuture.uniWhenCompleteStage(CompletableFuture.java:887) at java.base/java.util.concurrent.CompletableFuture.whenComplete(CompletableFuture.java:2325) at java.base/java.util.concurrent.CompletableFuture.whenComplete(CompletableFuture.java:144) at io.helidon.webclient.NettyClientHandler.channelRead0(NettyClientHandler.java:176) at io.helidon.webclient.NettyClientHandler.channelRead0(NettyClientHandler.java:61) at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:99) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:286) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436) at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:346) at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:333) at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:454) at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:290) at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) at io.netty.handler.logging.LoggingHandler.channelRead(LoggingHandler.java:280) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:286) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166) at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788) at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724) at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562) at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ... 5 more
排查与解决方法
原因分析
HTTP 302状态码表示临时重定向,Helidon WebClient默认不自动跟随重定向,因此当目标服务返回302时,客户端会直接抛出异常而不处理跳转。
解决方案
方案1:配置自动跟随重定向
在构建WebClient时添加followRedirects(true)配置,让客户端自动处理重定向请求:
WebClient client = WebClient.builder() .baseUri(producerUrl) .followRedirects(true) // 开启自动跟随重定向 .addMediaSupport(JsonpSupport.create()) .addHeader("Accept", "application/json") .build();
方案2:调整目标URL
检查目标服务的路由规则,可能是因为URL末尾的斜杠导致重定向。比如将producerUrl改为http://localhost:8080/books-db(去掉末尾斜杠),避免触发不必要的重定向。
方案3:手动处理重定向(可选)
如果需要自定义重定向逻辑,可以先获取响应的Location头,再手动发起新请求:
try { WebClient client = WebClient.builder() .baseUri(producerUrl) .addMediaSupport(JsonpSupport.create()) .addHeader("Accept", "application/json") .build(); // 先获取原始响应 WebClientResponse response = client.method("GET").request().get(); if (response.status() == 302) { String redirectUrl = response.headers().first("Location").get(); // 发起重定向请求 return client.get().uri(redirectUrl).request(JsonArray.class).get(); } else { return response.content().as(JsonArray.class).get(); } } catch(InterruptedException | ExecutionException e) { e.printStackTrace(); return null; }
内容的提问来源于stack exchange,提问作者Pratik Gaurav
相关产品推荐
相关产品推荐

