Spring RESTfulアプリケーションにResponseEntity <T>および@RestControllerを使用する場合


163

MVCおよびRestと一緒にSpring Framework 4.0.7を使用しています

私は次の人と安心して仕事ができます:

  • @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. とき固体理由または特定のシナリオの 1オプション他の上で強制的に使用する必要があります
  2. (1)が重要でない場合、どのようなアプローチが推奨され、その理由は何ですか。

回答:


213

ResponseEntityHTTP応答全体を表すことを意図しています。ステータスコード、ヘッダー、本文など、それに入るすべてのものを制御できます。

@ResponseBodyHTTP応答本文のマーカーであり、HTTP応答@ResponseStatusのステータスコードを宣言します。

@ResponseStatusあまり柔軟ではありません。メソッド全体をマークするため、ハンドラーメソッドが常に同じように動作することを確認する必要があります。また、ヘッダーを設定することはできません。HttpServletResponseまたはHttpHeadersパラメータが必要です。

基本的に、ResponseEntityより多くのことができます。


6
3回目の観察の良さ。ありがとう…そして私ResponseEntityはについて同じように考えました、それはより柔軟です。ちょうど私はについての疑問を持っていました@RestController。ありがとう
Manuel Jordan

55

Sotorios Delimanolisからの回答を完了するため。

これResponseEntityにより柔軟性が高まることは事実ですが、ほとんどの場合それを必要とせずResponseEntity、コントローラー内のあらゆる場所に配置されるため、読みにくく、理解しにくくなります。

エラーなどの特殊なケース(Not Found、Conflictなど)を処理する場合はHandlerExceptionResolver、Spring構成にを追加できます。したがって、コードでは、特定の例外(NotFoundExceptionたとえば)をスローし、ハンドラーで何を行うか(HTTPステータスを404に設定)を決定するだけで、コントローラーコードがより明確になります。


5
あなたの見解は、(@)ExceptionHandlerを使用して有効です。重要なのは、すべてを1つのメソッドで処理する場合(Try / Catch)、HttpEntityは適切に適合し、例外処理を再利用する場合(@)ExceptionHandlerは、多くの(@)RequestMappingに適合します。私はHttpEntityも好きです。私はHttpHeadersも使用できるからです。
Manuel Jordan

46

公式ドキュメントによると:@RestControllerアノテーションを使用したRESTコントローラーの作成

@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 <>(user、responseHeaders、HttpStatus.NOT_FOUND);を返す場合はどうなるか。@ResponseStatusが応答コードをさらに変更するかどうかだけを考えています。
Pratapi Hemant Patel

4
@Hemantは@ResponseStatus(HttpStatus.OK)戻っResponseEntity<>(user, responseHeaders, HttpStatus.NOT_FOUND)たときに無視されるようです。HTTPレスポンスは404
Danail

ResponseStatusのJavaDocsから。ステータスコードは、ハンドラメソッドが呼び出されたときにHTTP応答に適用され、{@ code ResponseEntity}や{@code "redirect:"}などの他の方法で設定されたステータス情報をオーバーライドします。
vzhemevko 2018

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

ソース-Spring in Action

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.