При использовании ResponseEntity и @RestController для приложений Spring RESTful

я работаю с 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) не имеет значения, какой подход предлагается и почему.

4 ответов


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

@ResponseBody является маркером для тела ответа HTTP и @ResponseStatus объявляет код состояния ответа HTTP.

@ResponseStatus не очень гибкий. Он отмечает весь метод, поэтому вы должны быть уверены, что ваш метод обработчика всегда будет вести себя одинаково. И вы все еще не можете установить заголовки. Вам понадобится HttpServletResponse или HttpHeaders параметр.

по сути, ResponseEntity позволяет сделать больше.


чтобы завершить ответ от Sotorios Delimanolis.

Это правда ResponseEntity дает вам больше гибкости, но в большинстве случаев вам это не понадобится, и вы получите эти ResponseEntity везде в вашем контроллере, что затрудняет чтение и понимание.

если вы хотите обрабатывать особые случаи, такие как ошибки (не найдено, конфликт и т. д.), вы можете добавить HandlerExceptionResolver к вашей конфигурации весны. Поэтому в вашем коде вы просто создаете определенное исключение (NotFoundException например) и решить, что делать в вашем обработчике (установка статуса HTTP на 404), что делает код контроллера более ясным.


согласно официальной документации:создание контроллеров REST с аннотацией @RestController

@RestController-это стереотипная аннотация, которая объединяет @ResponseBody и контроллер@. более того, это дает больше смысла вашей Контроллер, а также может нести дополнительную семантику в будущих выпусках из рамок.

кажется, что лучше использовать @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);

правильный REST API должен иметь компоненты ниже в ответе

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

источник - Весна в действии