§Handling errors
There are two main types of errors that an HTTP application can return - client errors and server errors. Client errors indicate that the connecting client has done something wrong, server errors indicate that there is something wrong with the server.
Play will in many circumstances automatically detect client errors - these include errors such as malformed header values, unsupported content types, and requests for resources that can’t be found. Play will also in many circumstances automatically handle server errors - if your action code throws an exception, Play will catch this and generate a server error page to send to the client.
The interface through which Play handles these errors is HttpErrorHandler
. It defines two methods, onClientError
, and onServerError
.
§Handling errors in a JSON API
By default, Play returns errors in a HTML format.
For a JSON API, it’s more consistent to return errors in JSON.
Play proposes an alternative HttpErrorHandler
implementation, named JsonHttpErrorHandler
, which will return errors formatted in JSON.
To use that HttpErrorHandler
implementation, you should configure the play.http.errorHandler
configuration property in application.conf
like this:
play.http.errorHandler = play.http.JsonHttpErrorHandler
§Supplying a custom error handler
If you’re using BuiltInComponents
to construct your app, override the httpRequestHandler
method to return an instance of your custom handler.
If you’re using runtime dependency injection (e.g. Guice), the error handler can be dynamically loaded at runtime. The simplest way is to create a class in the root package called ErrorHandler
that implements HttpErrorHandler
, for example:
import play.http.HttpErrorHandler;
import play.mvc.*;
import play.mvc.Http.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import javax.inject.Singleton;
@Singleton
public class ErrorHandler implements HttpErrorHandler {
public CompletionStage<Result> onClientError(RequestHeader request, int statusCode, String message) {
return CompletableFuture.completedFuture(
Results.status(statusCode, "A client error occurred: " + message)
);
}
public CompletionStage<Result> onServerError(RequestHeader request, Throwable exception) {
return CompletableFuture.completedFuture(
Results.internalServerError("A server error occurred: " + exception.getMessage())
);
}
}
If you don’t want to place your error handler in the root package, or if you want to be able to configure different error handlers for different environments, you can do this by configuring the play.http.errorHandler
configuration property in application.conf
:
play.http.errorHandler = "com.example.ErrorHandler"
§Extending the default error handler
Out of the box, Play’s default error handler provides a lot of useful functionality. For example, in dev mode, when a server error occurs, Play will attempt to locate and render the piece of code in your application that caused that exception, so that you can quickly see and identify the problem. You may want to provide custom server errors in production, while still maintaining that functionality in development. To facilitate this, Play provides a DefaultHttpErrorHandler
that has some convenience methods that you can override so that you can mix in your custom logic with Play’s existing behavior.
For example, to just provide a custom server error message in production, leaving the development error message untouched, and you also wanted to provide a specific forbidden error page:
import com.typesafe.config.Config;
import play.*;
import play.api.OptionalSourceMapper;
import play.api.UsefulException;
import play.api.routing.Router;
import play.http.DefaultHttpErrorHandler;
import play.mvc.Http.*;
import play.mvc.*;
import javax.inject.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
@Singleton
public class ErrorHandler extends DefaultHttpErrorHandler {
@Inject
public ErrorHandler(Config config, Environment environment,
OptionalSourceMapper sourceMapper, Provider<Router> routes) {
super(config, environment, sourceMapper, routes);
}
protected CompletionStage<Result> onProdServerError(RequestHeader request, UsefulException exception) {
return CompletableFuture.completedFuture(
Results.internalServerError("A server error occurred: " + exception.getMessage())
);
}
protected CompletionStage<Result> onForbidden(RequestHeader request, String message) {
return CompletableFuture.completedFuture(
Results.forbidden("You're not allowed to access this resource.")
);
}
}
Checkout the full API documentation for DefaultHttpErrorHandler
to see what methods are available to override, and how you can take advantage of them.
Next: Testing your application