web-dev-qa-db-fra.com

Lorsque vous utilisez ResponseEntity <T> et @RestController pour les applications Spring RESTful

Je travaille avec Spring Framework 4.0.7, avec MVC et Rest

Je peux travailler en paix avec:

  • @Controller
  • ResponseEntity<T>

Par exemple:

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

Avec la méthode (juste pour créer)

@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);
}

retourner quelque chose

@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);
}

Fonctionne bien

Je peux faire la même chose avec:

  • @RestController (Je sais que c'est la même chose que @Controller + @ResponseBody)
  • @ResponseStatus

Par exemple:

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

Avec la méthode (juste pour créer)

@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());
}

retourner quelque chose

@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;
}

Mes questions sont:

  1. quand pour une raison solide ou scénario spécifique une option doit être obligatoirement utilisée sur l'autre
  2. Si (1) n'a pas d'importance, quelle approche est suggérée et pourquoi.
143
Manuel Jordan

ResponseEntity est censé représenter la totalité de la réponse HTTP. Vous pouvez contrôler tout ce qui y va: code d'état, en-têtes et corps.

@ResponseBody est un marqueur pour le corps de la réponse HTTP et @ResponseStatus déclare le code d'état de la réponse HTTP.

@ResponseStatus n'est pas très flexible. Elle marque toute la méthode, vous devez donc vous assurer que votre méthode de gestionnaire se comportera toujours de la même manière. Et vous ne pouvez toujours pas définir les en-têtes. Vous aurez besoin du paramètre HttpServletResponse ou d'un paramètre HttpHeaders.

En gros, ResponseEntity vous permet de faire plus.

187

Pour compléter la réponse de Sotorios Delimanolis.

Il est vrai que ResponseEntity vous donne plus de flexibilité, mais dans la plupart des cas, vous n'en aurez pas besoin et vous vous retrouverez avec ces ResponseEntity partout dans votre contrôleur, ce qui rend la lecture et la compréhension difficiles.

Si vous souhaitez gérer des cas particuliers tels que des erreurs (non trouvé, conflit, etc.), vous pouvez ajouter un HandlerExceptionResolver à votre configuration Spring. Ainsi, dans votre code, il vous suffit de lancer une exception spécifique (NotFoundException par exemple) et de décider quoi faire dans votre gestionnaire (attribuer le statut HTTP à 404), ce qui rend le code du contrôleur plus clair.

48
Matt

Selon la documentation officielle: Création de REST contrôleurs avec l'annotation @RestController

@RestController est une annotation de stéréotype qui combine @ResponseBody et @Controller. Plus que cela, il donne plus de sens à votre contrôleur et peut également porter une sémantique supplémentaire dans les prochaines versions du framework.

Il semble préférable d’utiliser @RestController pour plus de clarté, mais vous pouvez aussi le combiner avec ResponseEntity pour plus de souplesse en cas de besoin ( Selon le tutoriel officiel et le code ici et ma question pour le confirmer ).

Par exemple:

@RestController
public class MyController {

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

        return user;
    }

}

est le même que:

@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);
    }

}

De cette façon, vous ne pouvez définir ResponseEntity que lorsque cela est nécessaire.

Mettre à jour

Vous pouvez utiliser ceci:

    return ResponseEntity.ok().headers(responseHeaders).body(user);
39
Danail

Une API REST appropriée doit avoir les composants suivants en réponse

  1. Code de statut
  2. Corps de réponse
  3. Emplacement de la ressource qui a été modifié (par exemple, si une ressource était créée, le client serait intéressé à connaître l'URL de cet emplacement)

ResponseEntity avait pour objectif principal de proposer l'option 3, les options de repos pouvant être réalisées sans ResponseEntity.

Par conséquent, si vous souhaitez indiquer l'emplacement de la ressource, utilisez plutôt ResponseEntity, sinon vous pouvez l'éviter.

Prenons un exemple où une API est modifiée pour fournir toutes les options mentionnées.

// 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);
}

Source - Spring in Action

9
Gautam Tadigoppula