当对Spring RESTful应用程序使用ResponseEntity <T>和@RestController时


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)无关紧要,则建议采用哪种方法以及为什么。

Answers:


213

ResponseEntity旨在表示整个HTTP响应。您可以控制其中的所有内容:状态码,标头和正文。

@ResponseBody是HTTP响应正文的标记,并@ResponseStatus声明HTTP响应的状态代码。

@ResponseStatus不是很灵活。它标记了整个方法,因此您必须确保您的处理程序方法始终具有相同的行为。而且您仍然无法设置标题。您需要HttpServletResponseHttpHeaders参数。

基本上,ResponseEntity您可以做更多的事情。


6
关于第三个观察的要点。谢谢...,我也有同样的想法ResponseEntity,它更加灵活。只是我感到怀疑@RestController。谢谢
Manuel Jordan

55

要完成Sotorios Delimanolis的答案。

的确,它ResponseEntity为您提供了更多的灵活性,但是在大多数情况下,您将不需要它,并且最终ResponseEntity在控制器中到处都有这些内容,因此很难阅读和理解。

如果您想处理诸如错误(未找到,冲突等)之类的特殊情况,则可以HandlerExceptionResolver在Spring配置中添加一个。因此,在您的代码中,您仅引发了特定的异常(NotFoundException例如),并决定在处理程序中要做什么(将HTTP状态设置为404),从而使Controller代码更加清晰。


5
您的观点与(@)ExceptionHandler一起使用是有效的。关键是:如果您想用一种方法(Try / Catch)处理所有事务,那么HttpEntity非常合适,如果您想对许多(@)RequestMapping进行重用异常处理(@)ExceptionHandler,则非常合适。我喜欢HttpEntity,因为我也可以使用HttpHeaders。
曼努埃尔·乔丹

46

根据官方文档:使用@RestController注释创建REST控制器

@RestController是将@ResponseBody和@Controller组合在一起的构造型注释。不仅如此,它还为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)。HTTP响应是404
Danail,2016年

来自ResponseStatus的JavaDocs。调用处理程序方法时,状态代码将应用于HTTP响应,并覆盖通过其他方式设置的状态信息,例如{@code ResponseEntity}或{@code“ redirect:”}。
vzhemevko

14

适当的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);
}

来源-行动中的春天

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.