HttpClient 在响应代码 404 上抛出异常。这是设计使然还是我做错了什么?

HttpClient throws exception on response code 404. Is this by design or am I doing something wrong?

我今天开始使用 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,消息 "Not found",然后我就完成了。

但是,相反,我得到:

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

at io.micronaut.http.client.DefaultHttpClient.channelRead0(DefaultHttpClient.java:1799)
at io.micronaut.http.client.DefaultHttpClient.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.run(SingleThreadEventExecutor.java:918)
at io.netty.util.internal.ThreadExecutorMap.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:834)

我找到了描述 An exception that occurs when a response returns an error code equal to or greater than 400.

HttpClientResponseException 文档

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

我是不是设置有误?

我认为您的代码没有问题。此特定异常的文档明确指出:

An exception that occurs when a response returns an error code equal to or greater than 400.

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

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

这个异常被进一步传递,看起来它最终被抛到了某个地方。就是这样工作的,这是作者的设计。如果你不喜欢它,你可以随时使用旧的内置 HttpURLConnection, a new HttpClient from Java 9 or something 3rd party like OkHttp.