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

Playframework自定义Action无法捕获部分异常的求助

嘿,我来帮你捋捋这个问题~ 你遇到的新异常捕获不到,大概率是因为异步异常的处理方式不对,再加上可能对Play框架里的异常捕获逻辑有点误解,我给你拆解下:

首先,为啥新异常抓不到?

你的try-catch只能捕获delegate.call(ctx)同步执行时抛出的异常,但如果你的业务逻辑里有异步操作(比如数据库查询是异步返回CompletionStage的),那异常会被包裹在这个异步结果里,根本进不了外层的try-catch。另外还要确认两点:

  • 你的新异常是不是PersistenceException的子类?如果不是,原来的catch (PersistenceException e)当然抓不到,得单独加对应的判断分支
  • 新异常是不是运行时异常?如果是检查型异常,得确保它被正确抛出或者在异步流程里被封装成运行时异常

然后,函数式风格下的正确异常处理写法

你现在用的是Action.Simple,但Play更推荐用函数式的HttpErrorHandler来统一处理异常,或者修正你的Action写法,正确处理异步异常。我给你两个方案:

方案1:修正你的Action.Simple代码(处理异步异常)

把原来的try-catch改成在CompletionStage上用exceptionally处理,这样不管是同步还是异步抛出的异常都能抓到:

public class ErrorHandler extends Action.Simple {
    private final Logger logger = Logger.of(this.getClass());

    @Override
    public CompletionStage<Result> call(Http.Context ctx) {
        // 去掉外层try-catch,改用CompletionStage的exceptionally处理异常
        return delegate.call(ctx)
                .exceptionally(throwable -> {
                    logger.error("Error occurred", throwable);
                    
                    if (throwable instanceof NoResultException) {
                        return notFound(new RecordNotFoundResponse(ctx.session().get("lang")).toJson());
                    } 
                    // 加上你的新异常判断
                    else if (throwable instanceof YourNewException) {
                        return status(400, new YourNewExceptionResponse(ctx.session().get("lang")).toJson());
                    }
                    else if (throwable instanceof PersistenceException) {
                        return status(409, new ConflictResponse(ctx.session().get("lang")).toJson());
                    } 
                    // 兜底处理未知异常
                    else {
                        return internalServerError(Json.obj("error", "Unexpected error"));
                    }
                });
    }
}

方案2:用Play推荐的HttpErrorHandler(函数式风格)

这个是更贴合Play函数式编程理念的方式,全局统一处理所有异常:

public class CustomErrorHandler implements HttpErrorHandler {
    private final Logger logger = Logger.of(this.getClass());

    // 处理客户端错误(比如404、400这类)
    @Override
    public CompletionStage<Result> onClientError(Http.RequestHeader request, int statusCode, String message) {
        return CompletableFuture.completedFuture(
                status(statusCode, Json.obj("error", message))
        );
    }

    // 处理服务器端异常(就是你要捕获的那些自定义异常)
    @Override
    public CompletionStage<Result> onServerError(Http.RequestHeader request, Throwable throwable) {
        return CompletableFuture.supplyAsync(() -> {
            logger.error("Server error occurred", throwable);
            
            if (throwable instanceof NoResultException) {
                return notFound(new RecordNotFoundResponse(request.session().get("lang")).toJson());
            } 
            else if (throwable instanceof YourNewException) {
                return status(400, new YourNewExceptionResponse(request.session().get("lang")).toJson());
            }
            else if (throwable instanceof PersistenceException) {
                return status(409, new ConflictResponse(request.session().get("lang")).toJson());
            } 
            else {
                return internalServerError(Json.obj("error", "Internal server error"));
            }
        });
    }
}

写完这个类后,还要在application.conf里配置它,让Play加载:

play.http.errorHandler = "com.yourpackage.CustomErrorHandler"

最后再提醒你几个小细节

  • 如果你用的是Play 2.6+,推荐用Http.Request而不是Http.Context,后者已经逐渐被废弃了
  • 异常处理时尽量明确捕获具体异常,别直接抓Exception,避免吞掉未知错误
  • 可以用throwable.getCause()看看是不是异常被包装了,有时候底层异常会被外层包裹一层

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 03:09:22