在 Spring RESTful 应用程序中使用 ResponseEntity<T> 和 @RestController 时

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26549379/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 00:08:48  来源:igfitidea点击:

When use ResponseEntity<T> and @RestController for Spring RESTful applications

springspring-mvcspring-3spring-4

提问by Manuel Jordan

I am working with Spring Framework 4.0.7, together with MVC and Rest

我正在使用 Spring Framework 4.0.7,以及 MVC 和 Rest

I can work in peace with:

我可以安心地工作:

  • @Controller
  • ResponseEntity<T>
  • @Controller
  • ResponseEntity<T>

For example:

例如:

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

With the method (just to create)

用方法(只是为了创建)

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

to return something

归还某物

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

Works fine

工作正常

I can do the same with:

我可以做同样的事情

  • @RestController(I know it is the same than @Controller+ @ResponseBody)
  • @ResponseStatus
  • @RestController(我知道它与@Controller+相同@ResponseBody
  • @ResponseStatus

For example:

例如:

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

With the method (just to create)

用方法(只是为了创建)

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

to return something

归还某物

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

My questions are:

我的问题是:

  1. when for a solid reasonor specific scenarioone option must be used mandatorily over the other
  2. If (1) does not matter, what approach is suggested and why.
  1. 出于可靠的原因特定情况下必须强制使用一个选项而不是另一个选项时
  2. 如果 (1) 无关紧要,建议采用什么方法以及为什么。

回答by Sotirios Delimanolis

ResponseEntityis meant to represent the entire HTTP response. You can control anything that goes into it: status code, headers, and body.

ResponseEntity表示整个 HTTP 响应。您可以控制进入其中的任何内容:状态代码、标题和正文。

@ResponseBodyis a marker for the HTTP response body and @ResponseStatusdeclares the status code of the HTTP response.

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

@ResponseStatusisn't very flexible. It marks the entire method so you have to be sure that your handler method will always behave the same way. And you still can't set the headers. You'd need the HttpServletResponseor a HttpHeadersparameter.

@ResponseStatus不是很灵活。它标记了整个方法,因此您必须确保您的处理程序方法将始终以相同的方式运行。而且您仍然无法设置标题。你需要HttpServletResponseor 一个HttpHeaders参数。

Basically, ResponseEntitylets you do more.

基本上,ResponseEntity让你做更多。

回答by Matt

To complete the answer from Sotorios Delimanolis.

完成 Sotorios Delimanolis 的回答。

It's true that ResponseEntitygives you more flexibility but in most cases you won't need it and you'll end up with these ResponseEntityeverywhere in your controller thus making it difficult to read and understand.

这确实ResponseEntity为您提供了更大的灵活性,但在大多数情况下,您不需要它,并且最终会ResponseEntity在控制器中随处可见这些,从而使其难以阅读和理解。

If you want to handle special cases like errors (Not Found, Conflict, etc.), you can add a HandlerExceptionResolverto your Spring configuration. So in your code, you just throw a specific exception (NotFoundExceptionfor instance) and decide what to do in your Handler (setting the HTTP status to 404), making the Controller code more clear.

如果您想处理诸如错误(未找到、冲突等)之类的特殊情况,您可以将 a 添加HandlerExceptionResolver到您的 Spring 配置中。因此,在您的代码中,您只需抛出一个特定的异常(NotFoundException例如)并决定在您的 Handler 中做什么(将 HTTP 状态设置为 404),使 Controller 代码更加清晰。

回答by Danail

According to official documentation: Creating REST Controllers with the @RestController annotation

根据官方文档:Creating REST Controllers with the @RestController annotation

@RestController is a stereotype annotation that combines @ResponseBody and @Controller. More than that, it gives more meaning to your Controller and also may carry additional semantics in future releases of the framework.

@RestController 是结合@ResponseBody 和@Controller 的构造型注解。更重要的是,它为您的控制器赋予了更多意义,并且还可能在框架的未来版本中携带额外的语义。

It seems that it's best to use @RestControllerfor clarity, but you can also combineit with ResponseEntityfor flexibility when needed (According to official tutorialand the code hereand my question to confirm that).

@RestController为了清楚起见,似乎最好使用它,但您也可以在需要时其与ResponseEntity灵活性结合使用(根据官方教程此处的代码以及我的问题来确认)。

For example:

例如:

@RestController
public class MyController {

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

        return user;
    }

}

is the same as:

是相同的:

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

}

This way, you can define ResponseEntityonly when needed.

这样,您可以ResponseEntity仅在需要时进行定义。

Update

更新

You can use this:

你可以使用这个:

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

回答by Gautam Tadigoppula

A proper REST API should have below components in response

适当的 REST API 应具有以下组件作为响应

  1. Status Code
  2. Response Body
  3. Location to the resource which was altered(for example, if a resource was created, client would be interested to know the url of that location)
  1. 状态码
  2. 响应体
  3. 被更改的资源的位置(例如,如果创建了一个资源,客户端将有兴趣知道该位置的 url)

The main purpose of ResponseEntity was to provide the option 3, rest options could be achieved without ResponseEntity.

ResponseEntity 的主要目的是提供选项 3,其他选项可以在没有 ResponseEntity 的情况下实现。

So if you want to provide the location of resource then using ResponseEntity would be better else it can be avoided.

因此,如果您想提供资源的位置,那么使用 ResponseEntity 会更好,否则可以避免。

Consider an example where a API is modified to provide all the options mentioned

考虑一个修改 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);
}

Source - Spring in Action

来源 - Spring 在行动