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

Call manually PlayFramework custom error page

我在 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 的错误页面未显示。我只看到文字 "Forbidden"。

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

我有一个类似的问题,我通过覆盖 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)。

我找到了一种通过将客户端错误注入所需元素来直接调用客户端错误的方法。我最终得到了以下解决方案(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.");
    }
}