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
相关产品推荐
相关产品推荐

