手动调用 PlayFramework 自定义错误页面

Posted

技术标签:

【中文标题】手动调用 PlayFramework 自定义错误页面【英文标题】:Call manually PlayFramework custom error page 【发布时间】:2016-04-07 07:24:25 【问题描述】:

我在 PlayFramework 中实现了自定义页面,就像文档中所说的那样:https://www.playframework.com/documentation/2.4.x/JavaErrorHandling

我在 application.conf 中添加了对 ErrorHandler 的引用

play.http.errorHandler = "com.company.ErrorHandler"

我自己实现了 ErrorHandler:

public class ErrorHandler extends DefaultHttpErrorHandler 

    @Inject
    public ErrorHandler(Configuration configuration, Environment environment, OptionalSourceMapper sourceMapper, Provider<Router> routes) 
        super(configuration, environment, sourceMapper, routes);
    

    @Override
    public F.Promise<Result> onClientError(Http.RequestHeader requestHeader, int errorCode, String message) 
        Logger.debug("Error: onClientError : " + errorCode + ", message: " + message);
        return super.onClientError(requestHeader, errorCode, message);
    

    @Override
    public F.Promise<Result> onServerError(Http.RequestHeader request, Throwable exception) 
        Logger.debug("Error: onServerError general");
        return F.Promise.pure(redirect(com.company.routes.ErrorController.serverErrorPage()));
    

    @Override
    protected F.Promise<Result> onBadRequest(Http.RequestHeader request, String message) 
        Logger.debug("Error: onBadRequest, message: " + message);
        return F.Promise.pure(redirect(com.company.routes.ErrorController.badRequestPage()));
    

    @Override
    protected F.Promise<Result> onForbidden(Http.RequestHeader request, String message) 
        Logger.debug("Error: onForbidden, message: " + message);
        return F.Promise.pure(redirect(com.company.routes.ErrorController.forbiddenPage()));
    

    @Override
    protected F.Promise<Result> onNotFound(Http.RequestHeader request, String message) 
        Logger.debug("Error: onNotFound, message: " + message);
        return F.Promise.pure(redirect(com.company.routes.ErrorController.notFoundPage()));
    

    @Override
    protected F.Promise<Result> onOtherClientError(Http.RequestHeader request, int statusCode, String message) 
        Logger.debug("Error: onOtherClientError, message: " + message);
        return F.Promise.pure(redirect(com.company.routes.ErrorController.errorDefaultPage()));
    

当框架本身抛出错误时,自定义页面效果很好。

虽然我在从控制器重定向到错误页面时遇到问题。当我调用端点时:

public Result contact() 
    return Results.forbidden("Forbidden");

没有显示来自 ErrorHander 的错误页面。我只看到文字“禁止”。

如何在不显式重定向的情况下显示我的自定义错误页面?

【问题讨论】:

【参考方案1】:

我遇到了类似的问题,我通过覆盖 onClientError 并切换到不同的 HTTP 状态代码来解决它:

public class ErrorHandler extends DefaultHttpErrorHandler 

  @Inject
  public ErrorHandler(Configuration configuration, Environment environment,
        OptionalSourceMapper sourceMapper, Provider<Router> routes) 
      super(configuration, environment, sourceMapper, routes);
  

  @Override
  public Promise<Result> onClientError(RequestHeader request, int statusCode,
        String message) 
    switch (statusCode) 
    case Http.Status.BAD_REQUEST:
        Logger.info(CLASS_NAME + ".onBadRequest: " + message);
        return Promise.<Result> pure(
                Results.badRequest(views.html.error.render("Bad request")));
    case Http.Status.NOT_FOUND:
        Logger.info(CLASS_NAME + ".onNotFound: Requested page \""
                + request.uri() + "\" couldn't be found.");
        return Promise.<Result> pure(
                Results.notFound(views.html.error.render("Requested page \""
                        + request.uri() + "\" couldn't be found.")));
    case Http.Status.FORBIDDEN:
        Logger.info(CLASS_NAME + ".onForbidden: " + message);
        return Promise.<Result> pure(Results
                .forbidden("You're not allowed to access this resource."));
    default:
        Logger.info(CLASS_NAME + ".onClientError: HTTP status code "
                + statusCode + ", " + message);
        return Promise.<Result> pure(Results.status(statusCode,
                views.html.error.render("Internal server error")));
    
  

  @Override
  public Promise<Result> onServerError(RequestHeader request,
        Throwable exception) 
    Logger.info(CLASS_NAME + ".onServerError: Internal server error",
            exception);
    return Promise.<Result> pure(Results.internalServerError(
            views.html.error.render("Internal server error")));
  


也许它甚至是 Play 文档中的一个错误?至少在HttpErrorHandler 接口中,除了onServerErroronClientError (https://www.playframework.com/documentation/2.4.0/api/java/play/http/HttpErrorHandler.html) 之外,它没有谈论其他方法。

【讨论】:

这很好。但是,如果您检查 DefaultHttpErrorHandler 的底层实现,那么您会注意到它已经将 onClientError 中的调用重定向到其他方法,例如 for e.g. onBadRequest,onForbidden。我的问题是,尽管返回了结果。禁止()。自定义 ErrorHandler 未捕获该请求。 你似乎是对的。也许它适用于 Play 2.5 - 与 2.4 相比,它们在 DefaultHttpErrorHandler 中发生了很大变化。【参考方案2】:

我找到了一种通过将客户端错误注入所需元素来直接调用客户端错误的方法。我最终得到了以下解决方案(Play Framework 2.5):

public class ErrorHandler extends DefaultHttpErrorHandler 

    private ErrorController errorController;

    @Inject
    public ErrorHandler(Configuration configuration,
                        Environment environment,
                        OptionalSourceMapper sourceMapper,
                        Provider<Router> routes,
                        ErrorController errorController) 
        super(configuration, environment, sourceMapper, routes);

        this.errorController = errorController;
    

    @Override
    public CompletionStage<Result> onServerError(Http.RequestHeader request, Throwable exception) 
        Logger.debug("Error: onServerError general");
        return CompletableFuture.completedFuture(this.errorController.serverErrorPage());
    

    @Override
    protected CompletionStage<Result> onBadRequest(Http.RequestHeader request, String message) 
        Logger.debug("Error: onBadRequest, message: " + message);
        return CompletableFuture.completedFuture(this.errorController.badRequestPage());
    

    @Override
    protected CompletionStage<Result> onForbidden(Http.RequestHeader request, String message) 
        Logger.debug("Error: onForbidden, message: " + message);
        return CompletableFuture.completedFuture(this.errorController.forbiddenPage());
    

    @Override
    protected CompletionStage<Result> onNotFound(Http.RequestHeader request, String message) 
        Logger.debug("Error: onNotFound, message: " + message);
        return CompletableFuture.completedFuture(this.errorController.notFoundPage());
    

    @Override
    protected CompletionStage<Result> onOtherClientError(Http.RequestHeader request, int statusCode, String message) 
        Logger.debug("Error: onOtherClientError, message: " + message);
        return CompletableFuture.completedFuture(this.errorController.errorDefaultPage());
    

那我可以这样称呼它:

class SomeElement  

    private final DefaultHttpErrorHandler errorHandler;

    @Inject
    public SomeElement(DefaultHttpErrorHandler errorHandler) 
        this.errorHandler = errorHandler;
    

    public CompletionStage<Result> onAuthFailure(final Http.Context context,
                                                 final Optional<String> content) 
        return this.errorHandler.onClientError(context.request(), Http.Status.FORBIDDEN, "You don't have required permissions.");
    

【讨论】:

以上是关于手动调用 PlayFramework 自定义错误页面的主要内容,如果未能解决你的问题,请参考以下文章

playframework[1.2.4] 500个html页面

在 playframework 中重新加载自定义文件更改的应用程序

织梦CMS如何在自定义表单页调用arclist标签啊,怎么在自定义表单页调用无效!

如何解决直接自引用导致循环错误将ActorRef保存在playframework 2.4.6中的redis上

Log4j 自定义配置为每个级别分隔日志 - Playframework 1.2.5

dedecms自定义模型之独立模型在首页列表页内容调用内容