You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Quarkus Reactive Rest如何抛出NotFoundException返回404响应

原因说明

在CompletionStage的thenApply异步回调中直接抛出的NotFoundException,会被异步计算逻辑包装为嵌套异常,Quarkus RESTEasy Reactive默认的异常处理器无法直接识别这类嵌套在异步链中的404异常,会将其判定为未捕获的服务端异常,最终返回500状态码并打印错误日志。

实现方案

方案1:调整异步链返回逻辑(推荐,无额外配置)

不要在thenApply中直接抛出异常,改用thenCompose处理异步结果:当查询到Account时返回包装了Account对象的成功Future,当Account不存在时,显式返回携带NotFoundException的失败Future。这种方式抛出的异常不会被额外嵌套包装,框架可以直接识别并自动返回404响应,不会打印ERROR级别的异常栈。

@GET
@Path("/{id}")
public CompletionStage<Account> get(@PathParam("id") String id) {
    return accountProvider
            .get(id)
            .thenCompose(maybeAccount -> maybeAccount
                    .map(CompletableFuture::completedFuture)
                    .orElseGet(() -> CompletableFuture.failedFuture(new NotFoundException()))
            );
}

方案2:自定义异步异常映射器(适合批量处理同类场景)

如果项目中存在大量同类异步接口,可以注册全局异常处理器,解包异步链抛出的CompletionException,识别其中嵌套的NotFoundException并返回404响应:

import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
import java.util.concurrent.CompletionException;

@Provider
public class AsyncExceptionMapper implements ExceptionMapper<CompletionException> {
    @Override
    public Response toResponse(CompletionException e) {
        Throwable rootCause = e.getCause();
        if (rootCause instanceof NotFoundException) {
            return Response.status(Response.Status.NOT_FOUND).build();
        }
        // 其余异常交由框架默认异常处理器处理
        throw new RuntimeException(rootCause);
    }
}

注意:请确保代码中使用的NotFoundException是jakarta.ws.rs.NotFoundException(Quarkus 3.x版本基于Jakarta EE,包名前缀为jakarta;Quarkus 2.x版本对应包名为javax.ws.rs.NotFoundException),引入错包下的同名异常也会导致框架无法识别。

内容的提问来源于stack exchange,提问作者sam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 19:12:42