При використанні ResponseEntity <T> та @RestController для додатків Spring RESTful


163

Я працюю з Spring Framework 4.0.7, спільно з MVC та Rest

Я можу працювати в мирі з:

  • @Controller
  • ResponseEntity<T>

Наприклад:

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

За допомогою методу (просто для створення)

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

повернути щось

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

Добре працює

Я можу зробити те саме :

  • @RestController(Я знаю, це те саме, що і @Controller+ @ResponseBody)
  • @ResponseStatus

Наприклад:

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

За допомогою методу (просто для створення)

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

повернути щось

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

Мої запитання:

  1. коли з вагомих причин або конкретного сценарію один варіант повинен бути використаний обов'язково над іншим
  2. Якщо (1) не має значення, який підхід пропонується і чому.

Відповіді:


213

ResponseEntityозначає, що представляє всю відповідь HTTP. Ви можете контролювати все, що входить у нього: код статусу, заголовки та тіло.

@ResponseBodyє маркером для органу відповіді HTTP і @ResponseStatusоголошує код статусу відповіді HTTP.

@ResponseStatusне дуже гнучка. Він позначає весь метод, тому ви повинні бути впевнені, що ваш метод обробки завжди буде вести себе однаково. І ви все одно не можете встановити заголовки. Ви б потрібен HttpServletResponseабо з HttpHeadersпараметром.

В основному, ResponseEntityдозволяє зробити більше.


6
Хороший момент щодо третього спостереження. Дякую ... і я думав про те саме ResponseEntity, що це гнучкіше. Просто я був із сумнівом @RestController. Дякую
Мануель Йордан

55

Для завершення відповіді від Соторіоса Деліманоліса.

Це правда, що ResponseEntityдає вам більшу гнучкість, але в більшості випадків вам це не знадобиться, і ви в кінцевому підсумку з цим станете ResponseEntityскрізь у своєму контролері, що ускладнить читання та розуміння.

Якщо ви хочете опрацьовувати спеціальні випадки, такі як помилки (Not Found, Conflict тощо), ви можете додати HandlerExceptionResolverдо своєї Spring конфігурації. Отже, у своєму коді ви просто кидаєте конкретний виняток ( NotFoundExceptionнаприклад) і вирішуєте, що робити у своєму обробнику (встановивши статус HTTP на 404), зробивши код контролера більш зрозумілим.


5
Ваша точка зору дійсна при роботі з (@) ExceptionHandler. Справа в тому, що якщо ви хочете, щоб все оброблялося одним методом (Спробуйте / Ловити), HttpEntity добре підходить, якщо ви хочете повторно використовувати обробку винятків (@) ExceptionHandler для багатьох (@) RequestMapping добре підходить. Мені подобається HttpEntity, тому що я вмію працювати і з HttpHeaders.
Мануель Йордан

46

Згідно з офіційною документацією: Створення контролерів REST з анотацією @RestController

@RestController - стереотипна анотація, яка поєднує @ResponseBody та @Controller. Більше того, це надає більше значення вашому контролеру, а також може мати додаткову семантику в майбутніх випусках фреймворку.

Здається, що найкраще використовувати @RestControllerдля ясності, але ви також можете комбінувати його ResponseEntityдля гнучкості при необхідності ( Відповідно до офіційного підручника та коду, тут і мого питання, щоб підтвердити це ).

Наприклад:

@RestController
public class MyController {

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

        return user;
    }

}

те саме, що:

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

}

Таким чином, ви можете визначитись ResponseEntityлише за потреби.

Оновлення

Ви можете скористатися цим:

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

Що робити, якщо ми додали @ResponseStatus (HttpStatus.OK) до методу, але метод повертає повернення нового ResponseEntity <> (користувач, responseHeaders, HttpStatus.NOT_FOUND); Я просто думаю, що чи змінить @ResponseStatus код відповіді далі.
Pratapi Hemant Patel

4
@Hemant, здається, @ResponseStatus(HttpStatus.OK)ігнорується під час повернення ResponseEntity<>(user, responseHeaders, HttpStatus.NOT_FOUND). Відповідь HTTP404
Данаїл

З JavaDocs з ResponseStatus. Код статусу застосовується до відповіді HTTP, коли викликається метод обробника та переосмислюється інформація про стан, встановлена ​​іншими способами, наприклад {@code ResponseEntity} або {@code "redirect:"}.
вжемевко

14

Належний API REST повинен відповідати компонентам нижче

  1. Код статусу
  2. Орган реагування
  3. Розташування до ресурсу, який було змінено (наприклад, якщо ресурс створений, клієнту було б цікаво знати URL-адресу цього місця)

Основна мета ResponseEntity полягала в тому, щоб запропонувати варіант 3, варіанти відпочинку можна досягти без ResponseEntity.

Отже, якщо ви хочете вказати місце розташування ресурсу, то, використовуючи ResponseEntity, було б краще, інакше цього можна уникнути.

Розглянемо приклад, коли API модифікований для надання всіх згаданих параметрів

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

Джерело - Весна в дії

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.