Overview

This is another in a series of articles I am writing about creating application using Java Spring Boot. The most recent article can be found here and the code for that article can be found here.

The web service we created in previous articles always returned an HTTP 200 response, indicating success. But that will not always be the case. Sometimes, things can go wrong; for example, a user can enter bad input. In cases where everything is not OK, you will want to return a different HTTP response. HTTP response codes in over 400 indicate a problem.

In this article, I will show how to check for invalid input and return a BAD REQUEST response (HTTP 400).

Input, Output, and a Controller method

Let's start with a Controller method that adds two numbers together and two model classes: One that defines the input of the controller method and the other that defines the output. Input and output will both be passed as JSON, but we can define a Java method with the same structure as the JSON.

Here is a sample JSON input that includes two numbers (firstNumber and secondNumber) and the name of the person making the request:

{
    "firstNumber": 10,
    "secondNumber": 20,
    "personName": "David"
}

and here is a Java class that describes the JSON object:

public class AddNumbersInput {
    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;
    }
}

When we add together the two numbers, we will return JSON containing the sum and a friendly message to the caller, similar to the following:

{
    "sum": 30,
    "message": "The sum of 10 and 20 is 30,  David"
}

Here is a Java model class with the same structure:

public class AddNumbersOutput {
    private Integer sum;
    private String message;

public Integer getSum() {
        return sum;
    }
    public void setSum(Integer sum) {
        this.sum = sum;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    public AddNumbersOutput(Integer sum, String message) {
        super();
        this.sum = sum;
        this.message = message;
    }
}

Next, we create a controller class, to accept the input, add the numbers together, and return the output in JSON format:

    @PostMapping(path="AddNumbers", consumes = "application/json", produces = "application/json")
    public ResponseEntity<AddNumbersOutput> AddNumbersPost(@RequestBody AddNumbersInput input) {
        Integer firstNumber = input.getFirstNumber();
        Integer secondNumber = input.getSecondNumber();
        String personName = input.getPersonName();

        Integer sum = mathService.AddNumbers(firstNumber, secondNumber);
        String message = "The sum of " + firstNumber + " and " + secondNumber 
            + " is " + sum + ",  " + personName;
        AddNumbersOutput output = new AddNumbersOutput(sum, message);
        return new ResponseEntity<AddNumbersOutput>(output, HttpStatus.OK);
    }

A client can send an HTTP POST to the AddNumbers endpoint and will receive an HTTP 200 (OK) response with the expected JSON in the response body. Fig. 1 shows this response when we call the web service using Postman.

Good HTTP request and response
Fig. 1

But, if the client omits any of the required properties in the input JSON, we will not be able to generate a response. We can check for this early in the Controller method and construct an appropriate message and return a BAD REQUEST (400) HTTP response in this case.

    @PostMapping(path="AddNumbers", consumes = "application/json", produces = "application/json")
    public ResponseEntity AddNumbersPost(@RequestBody AddNumbersInput input) {
        Integer firstNumber = input.getFirstNumber();
        Integer secondNumber = input.getSecondNumber();
        String personName = input.getPersonName();

        if (firstNumber==null || secondNumber==null || personName==null) {
            String message = "Missing input";
            AddNumbersOutput output = new AddNumbersOutput(null, message);
            return new ResponseEntity(output, HttpStatus.BAD_REQUEST);
        }

        Integer sum = mathService.AddNumbers(firstNumber, secondNumber);
        String message = "The sum of " + firstNumber + " and " + secondNumber 
            + " is " + sum + ",  " + personName;
        AddNumbersOutput output = new AddNumbersOutput(sum, message);
        return new ResponseEntity(output, HttpStatus.OK);
    }

Bad HTTP request and response - missing input data
Fig. 2 shows this scenario.

Fig. 2

We may add our own business logic to determine what defines an error. We may, for example, decide that the name "Bob" is not a valid name and want to return an error if someone tries to pass "Bob" as the personName, as shown below:

    @PostMapping(path="AddNumbers", consumes = "application/json", produces = "application/json")
    public ResponseEntity AddNumbersPost(@RequestBody AddNumbersInput input) {
        Integer firstNumber = input.getFirstNumber();
        Integer secondNumber = input.getSecondNumber();
        String personName = input.getPersonName();

        if (firstNumber==null || secondNumber==null || personName==null) {
            String message = "Missing input";
            AddNumbersOutput output = new AddNumbersOutput(null, message);
            return new ResponseEntity(output, HttpStatus.BAD_REQUEST);
        }
        if (personName.compareTo("Bob")==0) {
            String message = "Bob is not welcome here";
            AddNumbersOutput output = new AddNumbersOutput(null, message);
            return new ResponseEntity(output, HttpStatus.BAD_REQUEST);
        }

        Integer sum = mathService.AddNumbers(firstNumber, secondNumber);
        String message = "The sum of " + firstNumber + " and " + secondNumber 
            + " is " + sum + ",  " + personName;
        AddNumbersOutput output = new AddNumbersOutput(sum, message);
        return new ResponseEntity(output, HttpStatus.OK);
    }

Fig. 3 shows this request and the resulting response.

Bad HTTP request and response - invalid personName Bob
Fig. 3

Conclusion

This is a contrived example, but we can add our own logic and return what we feel is appropriate. The main point is that if we have a problem (for example, bad inputs), we should return to the client an HTTP error code and we should return some useful information without revealing any sensitive data.

You can find the code for this article here