Konrad Konrad - 7 months ago 26
Java Question

Call manually PlayFramework custom error page

I implemented custom pages in PlayFramework just like documentation says: https://www.playframework.com/documentation/2.4.x/JavaErrorHandling

I've added reference to ErrorHandler in application.conf

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


and I've implemented ErrorHandler itself:

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()));
}
}


Custom pages works great when error it thrown by framework itself.

Although I have a problem with redirecting to error pages from controller. When I call end point:

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


The error page from ErrorHander isn't shown. I only see text "Forbidden".

How can I show my custom error page without explicitly redirecting to it?

Answer

I found a way to call client error directly by injecting it to desired element. I ended up with following solution (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());
    }
}

Then I can call it like:

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.");
    }
}