Reading JSON in Spring with @RequestBody

In previous video tutorials, I have shared with you how to read Request Path parameters, Query String parameters, and Form data. In this tutorial, you will learn how to read JSON from HTTP Post request body in Spring Boot application.

To read the body of an HTTP Post request, we will use the @RequestBody annotation provided by the Spring framework.

@RequestBody Annotation

When a client sends an HTTP request to a Spring application, the request may contain a request body with data in a specific format, such as JSON or XML. The @RequestBody annotation tells Spring to convert the request body data into an object of the specified type and pass it as a parameter to the method.

@PostMapping(path="/users")
public ResponseEntity createUser(@RequestBody User user) {
    return ResponseEntity.ok(HttpStatus.OK);
}

This code is an example of a Spring Web MVC Controller method that maps to a HTTP POST request for the /users resource. The method has a single parameter, which is annotated with @RequestBody, indicating that the method expects the HTTP request body to be mapped to a Java object of type User. The User object is created from the JSON payload that is sent as the body of the HTTP request.

The method returns a ResponseEntity object with a HttpStatus of OK. This response indicates that the request was successful, and the server is sending an empty response body with a 200 OK status code. This code can be used as a starting point for creating a RESTful API endpoint that accepts and processes JSON payloads.

When we use the @RequestBody annotation in Spring Framework, the content of the HTTP body is read and mapped to the Java object used as the method argument.

In order for Spring Framework to map JSON key-value pairs to the properties in the User class, the User class must have property names that exactly match those in the JSON payload.

public class User {
    private String firstName;
    private String lastName;
    private String email;
    private String password;
    private String repeatPassword;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getRepeatPassword() {
        return repeatPassword;
    }

    public void setRepeatPassword(String repeatPassword) {
        this.repeatPassword = repeatPassword;
    }
}

I hope this tutorial has been helpful to you. If you want to learn more about building web applications with Spring Framework, please check out the Spring Web MVC category.

Can we map JSON payload to nested Java objects using @RequestBody?

Yes, it is possible to map JSON payload to nested Java objects using @RequestBody. You can define nested classes within your main Java class and use them as method arguments with @RequestBody.

Let’s say you have a JSON payload like this:

{
    "name": "John Doe",
    "age": 30,
    "address": {
        "street": "123 Main St",
        "city": "Anytown",
        "state": "CA",
        "zip": "12345"
    }
}

And you have Java classes like this:

public class User {
    private String name;
    private int age;
    private Address address;
    // getters and setters
}

public class Address {
    private String street;
    private String city;
    private String state;
    private String zip;
    // getters and setters
}

You can map the JSON payload to a User object using @RequestBody like this:

@PostMapping("/users")
public ResponseEntity<User> createUser(@RequestBody User user) {
    // code to create user
    return ResponseEntity.ok(user);
}

When the createUser method is called with the JSON payload, Spring will automatically map the JSON properties to the corresponding fields in the User object, including the nested Address object.

Note that the property names in the JSON payload must match the field names in the Java classes for the mapping to work properly. If they don’t match, you can use the @JsonProperty annotation to specify the mapping between the JSON property and the Java field.

Can I use @RequestBody with a collection or array of objects?

Yes, you can use the @RequestBody annotation with a collection or array of objects in Spring MVC or Spring Boot applications.

When you use @RequestBody with a collection or array of objects, Spring will automatically convert the incoming JSON array into a corresponding Java collection or array.

Here’s an example of how you can use @RequestBody with a collection of objects:

@PostMapping("/users")
public ResponseEntity<String> addUser(@RequestBody List<User> users) {
    // Process the list of users
    return ResponseEntity.ok("Users added successfully");
}

In this example, the @RequestBody annotation is used with a List<User> parameter. When a POST request is made to the “/users” endpoint with a JSON array of user objects in the request body, Spring will automatically map the JSON to a List<User> object.

Similarly, you can use @RequestBody with an array of objects:

@PostMapping("/products")
public ResponseEntity<String> addProducts(@RequestBody Product[] products) {
    // Process the array of products
    return ResponseEntity.ok("Products added successfully");
}

In this example, the @RequestBody annotation is used with a Product[] parameter. When a POST request is made to the “/products” endpoint with a JSON array of product objects in the request body, Spring will automatically map the JSON to a Product[] array.

Note that when using @RequestBody with a collection or array of objects, the incoming JSON data must be in a specific format. For example, if you’re using JSON to send an array of User objects, the JSON data should look something like this:

[
    {
        "name": "John",
        "email": "[email protected]"
    },
    {
        "name": "Jane",
        "email": "[email protected]"
    }
]

Overall, using @RequestBody with a collection or array of objects in Spring is a powerful feature that can help you easily process and validate large amounts of data sent in HTTP requests.

How can we handle errors when using @RequestBody?

To handle errors when using @RequestBody, you can use Spring’s built-in exception handling mechanisms. Spring provides several annotations and classes that allow you to handle exceptions and return appropriate error responses to clients.

One way to handle errors when using @RequestBody is to use the @ExceptionHandler annotation to define methods that handle specific exceptions. For example, you can define a method that handles HttpMessageNotReadableException to handle cases where the request body cannot be read:

@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity<String> handleHttpMessageNotReadableException(HttpMessageNotReadableException ex) {
    return ResponseEntity.badRequest().body("Invalid request body");
}

In this example, the @ExceptionHandler annotation is used to define a method that handles HttpMessageNotReadableException. When this exception is thrown, Spring will call this method and return a 400 Bad Request response with a message indicating that the request body is invalid.

Another way to handle errors when using @RequestBody is to use the @Valid annotation to validate incoming data against constraints defined in your Java classes. For example, you can define a User class with validation constraints on its fields:

public class User {
    @NotNull
    @Size(min = 3, max = 20)
    private String username;

    @NotNull
    @Email
    private String email;

    // Getters and setters
}

In this example, the @NotNull and @Size annotations are used to define constraints on the username field, while the @NotNull and @Email annotations are used to define constraints on the email field.

When you use @Valid with @RequestBody, Spring will automatically validate incoming data against the constraints defined in your Java classes. If the data is invalid, Spring will throw a MethodArgumentNotValidException exception, which you can handle using @ExceptionHandler.

@PostMapping("/users")
public ResponseEntity<String> addUser(@Valid @RequestBody User user, BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
        // Handle validation errors
    }

    // Process the user
    return ResponseEntity.ok("User added successfully");
}

In this example, the @Valid annotation is used to validate the incoming User object against the constraints defined in the class. The BindingResult parameter is used to access any validation errors that may have occurred. If validation errors exist, you can handle them in your controller method and return an appropriate error response.

Read more about Exceptions Handling in Spring Boot.