Quando si utilizza ResponseEntity <T> e @RestController per le applicazioni Spring RESTful


163

Sto lavorando con Spring Framework 4.0.7, insieme a MVC e Rest

Posso lavorare in pace con:

  • @Controller
  • ResponseEntity<T>

Per esempio:

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

Con il metodo (solo per creare)

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

per restituire qualcosa

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

Funziona bene

Posso fare lo stesso con :

  • @RestController(So ​​che è lo stesso di @Controller+ @ResponseBody)
  • @ResponseStatus

Per esempio:

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

Con il metodo (solo per creare)

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

per restituire qualcosa

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

Le mie domande sono:

  1. quando per una ragione solida o uno scenario specifico un'opzione deve essere obbligatoriamente utilizzata rispetto all'altra
  2. Se (1) non ha importanza, quale approccio viene suggerito e perché.

Risposte:


213

ResponseEntityè pensato per rappresentare l'intera risposta HTTP. Puoi controllare tutto ciò che lo riguarda: codice di stato, intestazioni e corpo.

@ResponseBodyè un marcatore per il corpo della risposta HTTP e @ResponseStatusdichiara il codice di stato della risposta HTTP.

@ResponseStatusnon è molto flessibile. Contrassegna l'intero metodo, quindi devi essere sicuro che il metodo del gestore si comporterà sempre allo stesso modo. E non puoi ancora impostare le intestazioni. Avresti bisogno del parametro HttpServletResponseo HttpHeaders.

Fondamentalmente, ResponseEntityti consente di fare di più.


6
Un buon punto sulla terza osservazione. Grazie ... e ho pensato lo stesso ResponseEntity, è più flessibile. Stavo solo con il dubbio @RestController. Grazie
Manuel Jordan,

55

Per completare la risposta di Sotorios Delimanolis.

È vero che ResponseEntityti dà maggiore flessibilità, ma nella maggior parte dei casi non ne avrai bisogno e finirai con questi ResponseEntityovunque nel tuo controller, rendendo così difficile la lettura e la comprensione.

Se vuoi gestire casi speciali come errori (Not Found, Conflict, ecc.), Puoi aggiungere un HandlerExceptionResolveralla tua configurazione Spring. Quindi, nel tuo codice, devi solo lanciare un'eccezione specifica ( NotFoundExceptionad esempio) e decidere cosa fare nel tuo Handler (impostando lo stato HTTP su 404), rendendo più chiaro il codice del Controller.


5
Il tuo punto di vista è valido lavorando con (@) ExceptionHandler. Il punto è: se vuoi che tutto sia gestito in un solo metodo (Try / Catch) HttpEntity si adatta bene, se vuoi riutilizzare la gestione delle eccezioni (@) ExceptionHandler per molti (@) RequestMapping si adatta bene. Mi piace HttpEntity perché sono in grado di lavorare anche con HttpHeaders.
Manuel Jordan,

46

Secondo la documentazione ufficiale: creazione di controller REST con l'annotazione @RestController

@RestController è un'annotazione stereotipata che combina @ResponseBody e @Controller. Oltre a ciò, dà più significato al tuo Controller e può anche contenere semantica aggiuntiva nelle versioni future del framework.

Sembra che sia meglio usarlo @RestControllerper chiarezza, ma puoi anche combinarlo con ResponseEntityflessibilità quando necessario ( Secondo il tutorial ufficiale e il codice qui e la mia domanda per confermarlo ).

Per esempio:

@RestController
public class MyController {

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

        return user;
    }

}

equivale a:

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

}

In questo modo, è possibile definire ResponseEntitysolo quando necessario.

Aggiornare

Puoi usare questo:

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

Che cosa succede se abbiamo aggiunto @ResponseStatus (HttpStatus.OK) al metodo, ma il metodo restituisce restituisce il nuovo ResponseEntity <> (utente, responseHeaders, HttpStatus.NOT_FOUND); Sto solo pensando che se @ResponseStatus modificherà ulteriormente il codice di risposta.
Pratapi Hemant Patel,

4
@Hemant sembra @ResponseStatus(HttpStatus.OK)essere ignorato al tuo ritorno ResponseEntity<>(user, responseHeaders, HttpStatus.NOT_FOUND). La risposta HTTP è404
Danail

Da JavaDocs di ResponseStatus. Il codice di stato viene applicato alla risposta HTTP quando viene invocato il metodo del gestore e sovrascrive le informazioni sullo stato impostate con altri mezzi, come {@code ResponseEntity} o {@code "redirect:"}.
vzhemevko,

14

Un'API REST corretta dovrebbe avere sotto i componenti in risposta

  1. Codice di stato
  2. Corpo di risposta
  3. Posizione della risorsa che è stata modificata (ad esempio, se una risorsa fosse creata, il cliente sarebbe interessato a conoscere l'URL di quella posizione)

Lo scopo principale di ResponseEntity era fornire l'opzione 3, le opzioni di riposo potevano essere raggiunte senza ResponseEntity.

Quindi, se si desidera fornire l'ubicazione della risorsa, utilizzare ResponseEntity sarebbe meglio altrimenti può essere evitato.

Considera un esempio in cui un'API viene modificata per fornire tutte le opzioni menzionate

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

Fonte - Spring in Action

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.