6.17.4.2 Custom Exception Handler

Imagine your e-commerce app throws an OutOfStockException when a book is out of stock:

  1. public class OutOfStockException extends RuntimeException {
  2. }
  1. class OutOfStockException extends RuntimeException {
  2. }
  1. class OutOfStockException : RuntimeException()

Along with BookController:

  1. @Controller("/books")
  2. public class BookController {
  3. @Produces(MediaType.TEXT_PLAIN)
  4. @Get("/stock/{isbn}")
  5. Integer stock(String isbn) {
  6. throw new OutOfStockException();
  7. }
  8. }
  1. @Controller("/books")
  2. class BookController {
  3. @Produces(MediaType.TEXT_PLAIN)
  4. @Get("/stock/{isbn}")
  5. Integer stock(String isbn) {
  6. throw new OutOfStockException()
  7. }
  8. }
  1. @Controller("/books")
  2. class BookController {
  3. @Produces(MediaType.TEXT_PLAIN)
  4. @Get("/stock/{isbn}")
  5. internal fun stock(isbn: String): Int? {
  6. throw OutOfStockException()
  7. }
  8. }

The server returns a 500 (Internal Server Error) status code if you don’t handle the exception.

To respond with 400 Bad Request as the response when the OutOfStockException is thrown, you can register a ExceptionHandler:

  1. @Produces
  2. @Singleton
  3. @Requires(classes = {OutOfStockException.class, ExceptionHandler.class})
  4. public class OutOfStockExceptionHandler implements ExceptionHandler<OutOfStockException, HttpResponse> {
  5. private final ErrorResponseProcessor<?> errorResponseProcessor;
  6. public OutOfStockExceptionHandler(ErrorResponseProcessor<?> errorResponseProcessor) {
  7. this.errorResponseProcessor = errorResponseProcessor;
  8. }
  9. @Override
  10. public HttpResponse handle(HttpRequest request, OutOfStockException e) {
  11. return errorResponseProcessor.processResponse(ErrorContext.builder(request)
  12. .cause(e)
  13. .errorMessage("No stock available")
  14. .build(), HttpResponse.badRequest()); (1)
  15. }
  16. }
  1. @Produces
  2. @Singleton
  3. @Requires(classes = [OutOfStockException, ExceptionHandler])
  4. class OutOfStockExceptionHandler implements ExceptionHandler<OutOfStockException, HttpResponse> {
  5. private final ErrorResponseProcessor<?> errorResponseProcessor
  6. OutOfStockExceptionHandler(ErrorResponseProcessor<?> errorResponseProcessor) {
  7. this.errorResponseProcessor = errorResponseProcessor
  8. }
  9. @Override
  10. HttpResponse handle(HttpRequest request, OutOfStockException e) {
  11. errorResponseProcessor.processResponse(ErrorContext.builder(request)
  12. .cause(e)
  13. .errorMessage("No stock available")
  14. .build(), HttpResponse.badRequest()) (1)
  15. }
  16. }
  1. @Produces
  2. @Singleton
  3. @Requirements(
  4. Requires(classes = [OutOfStockException::class, ExceptionHandler::class])
  5. )
  6. class OutOfStockExceptionHandler(private val errorResponseProcessor: ErrorResponseProcessor<Any>) :
  7. ExceptionHandler<OutOfStockException, HttpResponse<*>> {
  8. override fun handle(request: HttpRequest<*>, exception: OutOfStockException): HttpResponse<*> {
  9. return errorResponseProcessor.processResponse(
  10. ErrorContext.builder(request)
  11. .cause(exception)
  12. .errorMessage("No stock available")
  13. .build(), HttpResponse.badRequest<Any>()) (1)
  14. }
  15. }
1The default ErrorResponseProcessor is used to create the body of the response