Bei Verwendung von ResponseEntity <T> und @RestController für Spring RESTful-Anwendungen

163

Ich arbeite mit Spring Framework 4.0.7 zusammen mit MVC und Rest

Ich kann in Frieden arbeiten mit:

  • @Controller
  • ResponseEntity<T>

Beispielsweise:

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

Mit der Methode (nur zum Erstellen)

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

etwas zurückbringen

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

Funktioniert gut

Ich kann dasselbe tun mit :

  • @RestController(Ich weiß, es ist das gleiche wie @Controller+ @ResponseBody)
  • @ResponseStatus

Beispielsweise:

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

Mit der Methode (nur zum Erstellen)

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

etwas zurückbringen

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

Meine Fragen sind:

  1. Wenn aus einem soliden Grund oder in einem bestimmten Szenario eine Option zwingend über der anderen verwendet werden muss
  2. Wenn (1) keine Rolle spielt, welcher Ansatz wird vorgeschlagen und warum.
Manuel Jordan
quelle

Antworten:

213

ResponseEntitysoll die gesamte HTTP-Antwort darstellen. Sie können alles steuern, was darin enthalten ist: Statuscode, Überschriften und Text.

@ResponseBodyist eine Markierung für den HTTP-Antworttext und @ResponseStatusdeklariert den Statuscode der HTTP-Antwort.

@ResponseStatusist nicht sehr flexibel. Es markiert die gesamte Methode, sodass Sie sicherstellen müssen, dass sich Ihre Handlermethode immer gleich verhält. Und Sie können die Header immer noch nicht setzen. Sie benötigen den Parameter HttpServletResponseoder HttpHeaders.

Grundsätzlich ResponseEntitykönnen Sie mehr tun.

Sotirios Delimanolis
quelle
6
Guter Punkt zur dritten Beobachtung. Vielen Dank ... und ich dachte das gleiche ResponseEntity, es ist flexibler. Ich hatte nur Zweifel @RestController. Vielen Dank
Manuel Jordan
55

Um die Antwort von Sotorios Delimanolis zu vervollständigen.

Das ResponseEntitygibt Ihnen zwar mehr Flexibilität, aber in den meisten Fällen werden Sie es nicht benötigen, und Sie werden diese ResponseEntityüberall in Ihrem Controller finden, was das Lesen und Verstehen erschwert.

Wenn Sie Sonderfälle wie Fehler (Nicht gefunden, Konflikt usw.) behandeln möchten, können Sie HandlerExceptionResolverIhrer Spring-Konfiguration eine hinzufügen . In Ihrem Code lösen Sie einfach eine bestimmte Ausnahme aus ( NotFoundExceptionzum Beispiel) und entscheiden, was in Ihrem Handler zu tun ist (Setzen des HTTP-Status auf 404), wodurch der Controller-Code klarer wird.

Matt
quelle
5
Ihre Sichtweise ist gültig, wenn Sie mit (@) ExceptionHandler arbeiten. Der Punkt ist: Wenn Sie möchten, dass alle in einer Methode behandelt werden (Try / Catch), passt HttpEntity gut, wenn Sie die Ausnahmebehandlung wiederverwenden möchten (@) ExceptionHandler für viele (@) RequestMapping passt gut. Ich mag HttpEntity, weil ich auch mit HttpHeaders arbeiten kann.
Manuel Jordan
46

Laut offizieller Dokumentation: Erstellen von REST-Controllern mit der Annotation @RestController

@RestController ist eine stereotype Annotation, die @ResponseBody und @Controller kombiniert. Darüber hinaus verleiht es Ihrem Controller mehr Bedeutung und kann in zukünftigen Versionen des Frameworks zusätzliche Semantik enthalten.

Es scheint, dass es @RestControlleraus Gründen der Klarheit am besten ist, es zu verwenden , aber Sie können es auch kombinieren, um es ResponseEntitybei Bedarf flexibel zu gestalten ( laut offiziellem Tutorial und dem Code hier und meiner Frage, um dies zu bestätigen ).

Beispielsweise:

@RestController
public class MyController {

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

        return user;
    }

}

ist das gleiche wie:

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

}

Auf diese Weise können Sie ResponseEntitynur bei Bedarf definieren .

Aktualisieren

Sie können dies verwenden:

    return ResponseEntity.ok().headers(responseHeaders).body(user);
Danail
quelle
Was ist, wenn wir @ResponseStatus (HttpStatus.OK) zur Methode hinzugefügt haben, die Methode jedoch eine neue ResponseEntity <> zurückgibt (user, responseHeaders, HttpStatus.NOT_FOUND); Ich denke nur, ob @ResponseStatus den Antwortcode weiter modifizieren wird.
Pratapi Hemant Patel
4
@Hemant scheint @ResponseStatus(HttpStatus.OK)ignoriert zu werden, wenn Sie zurückkehren ResponseEntity<>(user, responseHeaders, HttpStatus.NOT_FOUND). Die HTTP-Antwort ist404
Danail
Aus JavaDocs des ResponseStatus. Der Statuscode wird auf die HTTP-Antwort angewendet, wenn die Handlermethode aufgerufen wird, und überschreibt Statusinformationen, die auf andere Weise festgelegt wurden, z. B. {@code ResponseEntity} oder {@code "redirect:"}.
Vzhemevko
14

Eine ordnungsgemäße REST-API sollte als Antwort folgende Komponenten enthalten

  1. Statuscode
  2. Antwortkörper
  3. Speicherort der Ressource, die geändert wurde (wenn beispielsweise eine Ressource erstellt wurde, wäre der Client daran interessiert, die URL dieses Speicherorts zu kennen).

Der Hauptzweck von ResponseEntity bestand darin, die Option 3 bereitzustellen. Ruheoptionen konnten ohne ResponseEntity erreicht werden.

Wenn Sie also den Speicherort der Ressource angeben möchten, ist die Verwendung von ResponseEntity besser, da dies sonst vermieden werden kann.

Stellen Sie sich ein Beispiel vor, in dem eine API geändert wird, um alle genannten Optionen bereitzustellen

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

Quelle - Frühling in Aktion

Gautam Tadigoppula
quelle