Thursday, April 20, 2023

How to use @RequestBody and @ResponseBody Annotations in Spring? Example Tutorial

Hello Java programmers, if you are wondering how and when to use use the @RequestBody and @ResponseBody annotation in Java then you are at the right place. Earlier, I have shared how to create RESTful Web Service using Spring Boot and today I will teach you how and when to use these two crucial annotations @RequestBody and @ResponseBody. While developing client server Java application using Spring Framework you may need to connect the HTTP request and response body with the domain object while working on the REST API. The annotations @RequestBody and @ResponseBody in Spring were used to bind these HTTP requests and responses. In simple words, these are the annotations which converts JSON to your Java object and your data to JSON while sending to client. Let's look at these two Spring annotations in more detail.


How to use @RequestBody and @ResponseBody Annotations in Spring

As the name suggest @RequestBody annotation is used to parse the incoming HTTP request while @ResponseBody annotation is used to convert your object into HTTP response in the form client is expecting like JSON, XML, or simply text. This is the basic, now let's look at these two annotations in detail. 


1. @RequestBody annotation

The @RequestBody annotation links the HTTPRequest body to the domain object, in layman's words. Using HTTP Message Converters, the Spring framework automatically de-serializes incoming HTTPRequests and converts them to Java objects.

 To resolve the method argument based on the content type of the request, we send the body of the request via a HttpMessageConverter. If the preceding definitions appear confusing, the @RequestBody annotation allows us to get the request's content and transform it into a Java Object automatically.

Let's look at the procedure of registering a cricket player as an example.

For registration, API requires cricketer data.
The system will transmit cricketer data via HTTPRequest body if the cricketer fills out the whole registration form.



So, let's explain in terms of code

Below is our data model.


package com.restdao.mydata;

public class Registration {

 private String firstName;
 private String lastName;
 private String email;
 private int age;
 private String password;

 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 int getAge() {
  return age;
 }

 public void setAge(int age) {
  this.age = age;
 }

 public String getPassword() {
  return password;
 }

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


Now, let's create a controller and explain where exactly @RequestBody annotation is used.

Controller: 


@RestController
public class RegistrationController {

 @Autowired
 private CricketerService cricketerService;

 @PostMapping("/registration")
 public ResponseEntity < Cricketer > register(@RequestBody Registration registration) {
  Cricketer cricketer = cricketerService.saveCricketer(mapCricketerData(registration));
  return new ResponseEntity < Cricketer > (cricketer, HttpStatus.OK);
 }

 protected Cricketer mapCricketerData(Registration registration) {
  Cricketer cricketer = new Cricketer(registration.getFirstName(), 
registration.getLastName(), registration.getEmail()); cricketer.setAge(registration.getAge()); return cricketer; } }



If a suitable Java type is given, Spring will automatically deserialize the JSON and convert it into a Java object much like we manually do using Jackson or Gson as explained here. As seen in the code above, the Registration object is annotated with @RequestBody which means the PostRequest has a body that contains the JSON format of the Registration object which is captured as a parameter by method register via our @RequestBody annotation. 

Java will automatically deserialize the incoming JSON into a Registration object if the JSON is valid and as per the Object naming conventions. 

This all is done by our annotation.

If we test our application via postman or any other client and pass the below data as body,


{
  "firstName":"first",
  "lastName":"last",
  "email":"sample@www.iplt20.com",
   "age":"24"
}


we will get the below response,

application/json;charset=UTF-8
transfer-encoding:chunked
date:Tue, 10 April 2022 02:49:39 GMT
{
"id": 5,
"firstName": "first",
"lastName": "last",
"email": "sample@www.iplt20.com",
"age": 24
}

Spring automatically converts receiving JSON data in the HTTPRequestBody to the Registration java object, as seen in this example.





2. @ResponseBody annotation in Spring MVC

To put it another way, @ResponseBody instructs the Spring framework to serialize a return object into JSON or XML and return it as part of the HTTPResponse.

When interacting with the REST API in Spring 4.x, we should use @RestController on a class level rather than @ResponseBody on a method level.

@RestController is a constructed annotation with @Controller and @ResponseBody that is itself a meta-annotation.

In the previous example, our register function returns a Cricketer object, which we want to deliver in the HTTPResponse as JSON.

We may still specify the content type that our function returns when we use the @ResponseBody annotation.

The generated property of the @RequestMapping may be used for this. Note that annotations like @PostMapping, @GetMapping, etc. define aliases for that parameter.


As seen in our controller below, we have @RestController annotation so we don't need @ResponseBody anymore. The @RestController will automatically add @ResponseBody when needed. 


REST Controller: 


@RestController
public class RegistrationController {

 @Autowired
 private CricketerService cricketerService;

 @PostMapping("/registration")
 public ResponseEntity < Cricketer > register(@RequestBody Registration registration) {
  Cricketer cricketer = cricketerService.saveCricketer(mapCricketerData(registration));
  return new ResponseEntity < Cricketer > (cricketer, HttpStatus.OK);
 }

 protected Cricketer mapCricketerData(Registration registration) {
  Cricketer cricketer = new Cricketer(registration.getFirstName(), 
registration.getLastName(), registration.getEmail()); cricketer.setAge(registration.getAge()); return cricketer; } }


But, if @RestController for any reason could not be annotated at the class level, we can anyways use @ResponseBody as shown below.


@Controller
public class RegController {

 @Autowired
 private CricketerService cricketerService;

 @PostMapping("/new-registration")
 public @ResponseBody Cricketer register(@RequestBody Registration registration) {
  return cricketerService.saveCricketer(mapCustomerData(registration));
 }
}


As shown above, we dictate that we are sending back the Cricketer object as a response. With @ResponseBody annotation, java automatically serializes it into JSON and sends it back.

How to use @RequestBody and @ResponseBody Annotations in Spring? Example Tutorial


That's all about how to use Spring @RequestBody and @ResponseBody annotations in Java and Spring MVC. These annotations are crucial when using Spring and Spring Boot to develop our REST API. You can use them for parsing request data into your domain object as well as converting your domain object to HTTP response in the format your client wants like JSON, XML or TEXT. If you use @RestContorller which you should then you don't need to explicitly use @RequestBody and @ResponseBody, its already taken care. 

Other Spring and REST Resources you may like

Thanks a lot for reading this article so far. If you like this Java and Spring Boot tutorial then please share them with your friends and colleagues. If you have any questions or feedback then please drop a note. 

1 comment :

Anonymous said...

The blog post misses the mark on what are the reasons why a class and method name cannot be annotated with RestController and GetMapping/PostMapping, leading to needing to use ResponseBody?

As for the code...

"Preparing for an interview" where you're going to say to use a Class instead of a Record for a DAO or DTO, and a plain text password?

public record RegistrationDto (String firstName, String lastName, String email, String dateOfBirth, byte[] password) {}

The Controller should use Constructor Injection, rather than Field injection. You can also make the Return statement much more readable by using:

CricketerDto errorCricketerDto new CricketerDto();
try {
return ResponseEntity.ok(
cricketerService.registerCricketer(registrationDto));
} catch (SomeException e) {
errorCricketerDto.setErrorMessage("Message Related to Exception");
} catch (DateTimeParseException e) {
errorCricketerDto.setErrorMessage("Date of Birth not in the correct format");
}

return ResponseEntity.internalServerError()
.body(errorCricketerDto);
In the interests of "Low Coupling, High Cohesion", the mapCricketerData should be done in the CricketerService, along with any other data sanitisation. The method name saveCricketer be reserved for the CricketerRepository.

I don't think you should be encouraging, what seem to be AutoIncrement, IDs to be returned in the ResponseBody, but it's just an example.

I think you should also have included a link to a GitHub Repository (so users could set breakpoints in the their IDEs), or Docker Hub Image, and for the API and pre-filled JSON a PostMan Collection, SpringDoc-OpenApi (formally Swagger), or a curl command so users could easily build, run and test your example.

Post a Comment