首页 > 解决方案 > HttpClient 在响应代码 404 上引发异常。这是设计使然还是我做错了什么?

问题描述

我今天从 Micronaut 开始。我构建了这个控制器:

@Controller
public class MyController implements MyApi{

@Override
public String doit() {
    throw new NotFoundException();
    }
}

和这个异常处理程序:

@Produces
@Singleton
@Requires(classes = { NotFoundException.class, ExceptionHandler.class})
public class NotFoundExceptionHandler implements ExceptionHandler<NotFoundException, HttpResponse> {

@Override
public HttpResponse handle(HttpRequest request, NotFoundException exception) {
    return HttpResponseFactory.INSTANCE.status(HttpStatus.NOT_FOUND);
}

}

而这个测试:

@MicronautTest
public class MyControllerIT {

@Inject
@Client("/")
HttpClient client;

@Test
public void testHello() {
    HttpRequest<String> request = HttpRequest.GET("/myController");
    HttpResponse<String> body = client.toBlocking().exchange(request);

    assertThat(body.getStatus(), is(HttpStatus.NOT_FOUND));
    }
}

我的期望是它会通过:我会得到一个没有内容的响应,HTTP 代码 404,消息“未找到”,我会完成的。

但是,相反,我得到:

io.micronaut.http.client.exceptions.HttpClientResponseException: Not Found

at io.micronaut.http.client.DefaultHttpClient$10.channelRead0(DefaultHttpClient.java:1799)
at io.micronaut.http.client.DefaultHttpClient$10.channelRead0(DefaultHttpClient.java:1739)
at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:105)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
at io.micronaut.http.netty.stream.HttpStreamsHandler.channelRead(HttpStreamsHandler.java:185)
at io.micronaut.http.netty.stream.HttpStreamsClientHandler.channelRead(HttpStreamsClientHandler.java:180)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:102)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:102)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:438)
at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:328)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:302)
at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:253)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:287)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1421)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:930)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:697)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:632)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:549)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:511)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:918)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:834)

我发现这个HttpClientResponseException文档描述了An exception that occurs when a response returns an error code equal to or greater than 400.

这真的是预期的行为吗?当错误代码> 400时,它迫使我将其作为异常处理,这似乎有点奇怪。

我在设置中做错了吗?

标签: javamicronaut

解决方案


我认为您的代码没有问题。这个特殊例外的文档清楚地指出:

当响应返回等于或大于 400 的错误代码时发生的异常。

如果这对您不满意,我们可以深入挖掘。异常在DefaultHttpClient名为 的方法中的类中引发channelRead0。让我们看看我们能在那里找到什么。

boolean errorStatus = statusCode >= 400;
if (errorStatus) {
    emitter.onError(new HttpClientResponseException(response.getStatus().getReason(), response));
} else {
    emitter.onNext(response);
    emitter.onComplete();
}

这个异常被进一步传递,看起来它最终被抛出到某个地方。它只是这样工作,这是作者的设计。如果您不喜欢它,您可以随时使用旧的内置HttpURLConnection、Java 9 中的新HttpClient或像OkHttp这样的第 3 方。


推荐阅读