Overview

In a recent article, I showed you how to create and catch custom errors in a Java Spring Boot application. This article will expand on that and add code to catch built-in exceptions.

You can start with the code from the previous article, which is found here.

Some Error-Prone Code

Let's add some code to our application to perform arithmetic division. We will start with input and output model classes to allow our controller to send and receive JSON.

package com.dgtest.dgtest.models;

public class DivideNumbersInput {
    private Integer firstNumber;
    private Integer secondNumber;
    private String personName;

    public Integer getFirstNumber() {
        return firstNumber;
    }
    public void setFirstNumber(Integer firstNumber) {
        this.firstNumber = firstNumber;
    }

    public Integer getSecondNumber() {
        return secondNumber;
    }
    public void setSecondNumber(Integer secondNumber) {
        this.secondNumber = secondNumber;
    }

    public String getPersonName() {
        return personName;
    }
    public void setPersonName(String personName) {
        this.personName = personName;
    }
}
package com.dgtest.dgtest.models;

public class DivideNumbersOutput {
    private Integer quotient;
    private String message;

    public Integer getQuotient() {
        return quotient;
    }
    public void setQuotient(Integer quotient) {
        this.quotient = quotient;
    }

    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }

    public DivideNumbersOutput(Integer quotient, String message) {
        super();
        this.quotient = quotient;
        this.message = message;
    }
}

Next, we will add a DivideNumbers method to our service, including the interface from which the service inherits (and any other classes that implement that interface)

public interface MathService {
    Integer AddNumbers(Integer firstNumber, Integer secondNumber) throws MissingArgumentsException;
    Integer SubtractNumbers(Integer firstNumber, Integer secondNumber);
    Integer DivideNumbers(Integer firstNumber, Integer secondNumber);
}
@Qualifier("MathServiceImpl")
@Service
public class MathServiceImpl implements MathService {

    ...

    @Override
    public Integer DivideNumbers(Integer firstNumber, Integer secondNumber){
        Integer quotient = firstNumber / secondNumber;
        return quotient;
    }
}

Finally, we add a method to our controller that calls the service and returns the result wrapped in a ResponseEntity object.

@RequestMapping("math")
@RestController
public class MathController {

    ...

    @PostMapping(path="DivideNumbers", consumes = "application/json", produces = "application/json")
    public ResponseEntity<DivideNumbersOutput> DivideNumbersPost(@RequestBody DivideNumbersInput input) throws MissingArgumentsException {
        Integer firstNumber = input.getFirstNumber();
        Integer secondNumber = input.getSecondNumber();
        String personName = input.getPersonName();
        Integer quotient = mathService.DivideNumbers(firstNumber, secondNumber);
        String message = firstNumber + " divided by " + secondNumber 
            + " is " + quotient + ", " + personName;
        DivideNumbersOutput output = new DivideNumbersOutput(quotient, message);
        return new ResponseEntity<DivideNumbersOutput>(output, HttpStatus.OK);
    }
}

We can run the application and call it from a tool like Postman by sending an HTTP POST request to the math/DivideNumbers endpoint, as shown in Fig. 1.

DivideNumbers API call with 200 response
Fig. 1

This works fine unless we pass 0 as the second number. In that case, we get a generic exception, as shown in Fig. 2.

DivideNumbers API call with 200 response with Unhandled Exception
Fig. 2

As you can see, the API returns an HTTP 500 (Internal Server Error) that contains very little useful information. We can improve this API by returning something more useful to the client.

Catching Built-In Exceptions

We can handle these exceptions more gracefully by adding a try/catch structure around the code in our controller, as shown below:

    @PostMapping(path="DivideNumbers", consumes = "application/json", produces = "application/json")
    public ResponseEntity<DivideNumbersOutput> DivideNumbersPost(@RequestBody DivideNumbersInput input) throws MissingArgumentsException {
        Integer firstNumber = input.getFirstNumber();
        Integer secondNumber = input.getSecondNumber();
        String personName = input.getPersonName();
        Integer quotient = null;
        try {
            quotient = mathService.DivideNumbers(firstNumber, secondNumber);
        } catch (ArithmeticException e) {
            String message = "An error has occurred, " + personName + ": " + e.getMessage();
            DivideNumbersOutput output = new DivideNumbersOutput(null, message);
            return new ResponseEntity<DivideNumbersOutput>(output, HttpStatus.BAD_REQUEST);
        } catch (Exception e) {
            String message = "An error has occurred, " + personName + ": " + e.getMessage();
            DivideNumbersOutput output = new DivideNumbersOutput(null, message);
            return new ResponseEntity<DivideNumbersOutput>(output, HttpStatus.BAD_REQUEST);
        }
        String message = firstNumber + " divided by " + secondNumber 
            + " is " + quotient + ", " + personName;
        DivideNumbersOutput output = new DivideNumbersOutput(quotient, message);
        return new ResponseEntity<DivideNumbersOutput>(output, HttpStatus.OK);
    }

We have moved the call to the MathService method inside a TRY block and added two CATCH blocks. The first CATCH block will trap an Arithmetic error, which is what happens we try to divide by 0. The second CATCH block traps any other errors that occur.

With this code change, the results are more useful when a client passes a 0 as a denominator, as shown in Fig. 3.

DivideNumbers API call with 200 response with Exception Handling
Fig. 3

Conclusion

This article shows an example of gracefully handling errors in a Java Spring Boot REST API. You can find the code here.