When use ResponseEntity<T> and @RestController for Spring RESTful applications
Asked Answered
Q

6

224

I am working with Spring Framework 4.0.7, together with MVC and Rest

I can work in peace with:

  • @Controller
  • ResponseEntity<T>

For example:

@Controller
@RequestMapping("/person")
@Profile("responseentity")
public class PersonRestResponseEntityController {

With the method (just to create)

@RequestMapping(value="/", method=RequestMethod.POST)
public ResponseEntity<Void> createPerson(@RequestBody Person person, UriComponentsBuilder ucb){
    logger.info("PersonRestResponseEntityController  - createPerson");
    if(person==null)
        logger.error("person is null!!!");
    else
        logger.info("{}", person.toString());

    personMapRepository.savePerson(person);
    HttpHeaders headers = new HttpHeaders();
    headers.add("1", "uno");
    //http://localhost:8080/spring-utility/person/1
    headers.setLocation(ucb.path("/person/{id}").buildAndExpand(person.getId()).toUri());

    return new ResponseEntity<>(headers, HttpStatus.CREATED);
}

to return something

@RequestMapping(value="/{id}", method=RequestMethod.GET)
public ResponseEntity<Person> getPerson(@PathVariable Integer id){
    logger.info("PersonRestResponseEntityController  - getPerson - id: {}", id);
    Person person = personMapRepository.findPerson(id);
    return new ResponseEntity<>(person, HttpStatus.FOUND);
}

Works fine

I can do the same with:

  • @RestController (I know it is the same than @Controller + @ResponseBody)
  • @ResponseStatus

For example:

@RestController
@RequestMapping("/person")
@Profile("restcontroller")
public class PersonRestController {

With the method (just to create)

@RequestMapping(value="/", method=RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public void createPerson(@RequestBody Person person, HttpServletRequest request, HttpServletResponse response){
    logger.info("PersonRestController  - createPerson");
    if(person==null)
        logger.error("person is null!!!");
    else
        logger.info("{}", person.toString());

    personMapRepository.savePerson(person);
    response.setHeader("1", "uno");

    //http://localhost:8080/spring-utility/person/1
    response.setHeader("Location", request.getRequestURL().append(person.getId()).toString());
}

to return something

@RequestMapping(value="/{id}", method=RequestMethod.GET)
@ResponseStatus(HttpStatus.FOUND)
public Person getPerson(@PathVariable Integer id){
    logger.info("PersonRestController  - getPerson - id: {}", id);
    Person person = personMapRepository.findPerson(id);
    return person;
}

My questions are:

  1. when for a solid reason or specific scenario one option must be used mandatorily over the other
  2. If (1) does not matter, what approach is suggested and why.
Quittor answered 24/10, 2014 at 13:56 Comment(0)
T
287

ResponseEntity is meant to represent the entire HTTP response. You can control anything that goes into it: status code, headers, and body.

@ResponseBody is a marker for the HTTP response body and @ResponseStatus declares the status code of the HTTP response.

@ResponseStatus isn't very flexible. It marks the entire method so you have to be sure that your handler method will always behave the same way. And you still can't set the headers. You'd need the HttpServletResponse.

Basically, ResponseEntity lets you do more.

Triumph answered 24/10, 2014 at 15:17 Comment(1)
Good point about the third observation. Thank You… and I thought the same about ResponseEntity, it is more flexible. Just I was with the doubt about @RestController. Thank youQuittor
I
82

To complete the answer from Sotorios Delimanolis.

It's true that ResponseEntity gives you more flexibility but in most cases you won't need it and you'll end up with these ResponseEntity everywhere in your controller thus making it difficult to read and understand.

If you want to handle special cases like errors (Not Found, Conflict, etc.), you can add a HandlerExceptionResolver to your Spring configuration. So in your code, you just throw a specific exception (NotFoundException for instance) and decide what to do in your Handler (setting the HTTP status to 404), making the Controller code more clear.

Irrelevant answered 30/4, 2015 at 10:55 Comment(1)
Your point of view is valid working with (@)ExceptionHandler. The point is: if you want all handled in one method (Try/Catch) HttpEntity fits well, if you want reuse exception handling (@)ExceptionHandler for many (@)RequestMapping fits well. I like HttpEntity because I am able to work with HttpHeaders too.Quittor
T
61

According to official documentation: Creating REST Controllers with the @RestController annotation

@RestController is a stereotype annotation that combines @ResponseBody and @Controller. More than that, it gives more meaning to your Controller and also may carry additional semantics in future releases of the framework.

It seems that it's best to use @RestController for clarity, but you can also combine it with ResponseEntity for flexibility when needed (According to official tutorial and the code here and my question to confirm that).

For example:

@RestController
public class MyController {

    @GetMapping(path = "/test")
    @ResponseStatus(HttpStatus.OK)
    public User test() {
        User user = new User();
        user.setName("Name 1");

        return user;
    }

}

is the same as:

@RestController
public class MyController {

    @GetMapping(path = "/test")
    public ResponseEntity<User> test() {
        User user = new User();
        user.setName("Name 1");

        HttpHeaders responseHeaders = new HttpHeaders();
        // ...
        return new ResponseEntity<>(user, responseHeaders, HttpStatus.OK);
    }

}

This way, you can define ResponseEntity only when needed.

Update

You can use this:

    return ResponseEntity.ok().headers(responseHeaders).body(user);
Trothplight answered 6/11, 2016 at 21:29 Comment(3)
What if we have added @ResponseStatus(HttpStatus.OK) on the method, but method returns return new ResponseEntity<>(user, responseHeaders, HttpStatus.NOT_FOUND); I am just thinking that whether @ResponseStatus will modify the response code further.Twocycle
@Hemant seems that @ResponseStatus(HttpStatus.OK) is ignored when you return ResponseEntity<>(user, responseHeaders, HttpStatus.NOT_FOUND). The HTTP response is 404Trothplight
From JavaDocs of the ResponseStatus. The status code is applied to the HTTP response when the handler method is invoked and overrides status information set by other means, like {@code ResponseEntity} or {@code "redirect:"}.Distefano
F
29

A proper REST API should have below components in response

  1. Status Code
  2. Response Body
  3. Location to the resource which was altered(for example, if a resource was created, client would be interested to know the url of that location)

The main purpose of ResponseEntity was to provide the option 3, rest options could be achieved without ResponseEntity.

So if you want to provide the location of resource then using ResponseEntity would be better else it can be avoided.

Consider an example where a API is modified to provide all the options mentioned

// Step 1 - Without any options provided
@RequestMapping(value="/{id}", method=RequestMethod.GET)
public @ResponseBody Spittle spittleById(@PathVariable long id) {
  return spittleRepository.findOne(id);
}

// Step 2- We need to handle exception scenarios, as step 1 only caters happy path.
@ExceptionHandler(SpittleNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public Error spittleNotFound(SpittleNotFoundException e) {
  long spittleId = e.getSpittleId();
  return new Error(4, "Spittle [" + spittleId + "] not found");
}

// Step 3 - Now we will alter the service method, **if you want to provide location**
@RequestMapping(
    method=RequestMethod.POST
    consumes="application/json")
public ResponseEntity<Spittle> saveSpittle(
    @RequestBody Spittle spittle,
    UriComponentsBuilder ucb) {

  Spittle spittle = spittleRepository.save(spittle);
  HttpHeaders headers = new HttpHeaders();
  URI locationUri =
  ucb.path("/spittles/")
      .path(String.valueOf(spittle.getId()))
      .build()
      .toUri();
  headers.setLocation(locationUri);
  ResponseEntity<Spittle> responseEntity =
      new ResponseEntity<Spittle>(
          spittle, headers, HttpStatus.CREATED)
  return responseEntity;
}

// Step4 - If you are not interested to provide the url location, you can omit ResponseEntity and go with
@RequestMapping(
    method=RequestMethod.POST
    consumes="application/json")
@ResponseStatus(HttpStatus.CREATED)
public Spittle saveSpittle(@RequestBody Spittle spittle) {
  return spittleRepository.save(spittle);
}
Fibriform answered 19/8, 2018 at 10:28 Comment(0)
T
0

Node:

@RestController = @Controller + @ResponseBody**

When use only @Controller in your class also mark @ResponseBody-

example:

@ResponseBody
@Controller
    class EmployeeController{
}

If use only method so mark in method @ResponseBody.

Thickhead answered 30/1 at 6:8 Comment(0)
K
0

Considering the use of ResponseEntity to be the best practice, mainly the only inconvenience of ResponseEntity is what is montied by @matt

It's true that ResponseEntity gives you more flexibility but in most cases you won't need it and you'll end up with these ResponseEntity everywhere in your controller thus making it difficult to read and understand.

In order to overcome the readability issue, I use a class with a short name generally R with static functions as follows:

public class R {
  ...
  public static <T> ResponseEntity<T>  OK(T body)
  {
    return new ResponseEntity<T>(body,HttpStatus.OK);
  }
  public static <T> ResponseEntity<T>  CREATED(T body)
  {
    return new ResponseEntity<T>(body,HttpStatus.CREATED);
  }
  ...

You can implement all the http status in enum org.springframework.http.HttpStatus (a function for each status); on the other hand, you can use only the common ones such as this list https://mcmap.net/q/120319/-what-http-status-codes-should-programmers-be-concerned-with.

Finally, the controller is going to look like:

@RestController
public class MyContoller {
@GetMapping("api/stores")
public ResponseEntity<?>  listeStores()
{
    return R.OK(storesDao.findAll());
}

Note that you my need a second function for each http status if you want to handle headers:

public static <T> ResponseEntity<T>  OK(T body)
  {
    return new ResponseEntity<T>(body,HttpStatus.OK);
  }
public static <T> ResponseEntity<T>  OK(T body,HttpHeaders responseHeaders)
  {
    return ResponseEntity.ok().headers(responseHeaders).body(body);
  }
...
Kato answered 14/2 at 21:32 Comment(1)
first of all, methods in Java should be lower case, and your approach doesn't improve readabilityPringle

© 2022 - 2024 — McMap. All rights reserved.