ResponseEntityと@ResponseStatusの違いを調べました


## 入る前に
- 私は韓国人として、日本語の勉強とコンピュータの勉強を同時に行うために、ここに文章を書きます
- 翻訳機の助けを借りて書かれた文章なので、誤りがあるかもしれません
- 間違った部分についてのフィードバックはいつでも歓迎です
- 私と異なる意見もいつでも歓迎です
- 日本の開発者たちとコミュニケーションを取りたいです！

## 主題紹介

SpringでHTTP応答を設定するResponseEntityと@ResponseStatusオプションの違いについて調べました。

---

## ResponseEntity
```java
    @PostMapping()
    public ResponseEntity<String> startWeatherCrawlToday(){
        return ResponseEntity.ok("hello");
    }
```
上記のコードはHTTPステータスコードを200で返し、応答本文にhelloを含んでいます。

## @ ResponseStatus
```java
    @ResponseStatus(HttpStatus.OK)
    @PostMapping()
    public String startWeatherCrawlToday(){
        return "hello";
    }
```
上記のコードも同様にHTTPステータスコード200を返し、応答本文にhelloを含んでいます。
互いに何が違うのでしょうか？そして、どちらを使うべきでしょうか？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/d7f63e4e-81f6-3d64-a0cc-9e3b73d7b141.png)

ResponseStatusを開くと、次のようなコメントが出てきます。
順を追って解釈してみましょう。

---

## ResponseStatusを調べる

> メソッドまたは例外クラスに返されるステータス{@link #code}と{@link #reason}を表示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/326a47bd-87ff-e4b9-25d3-1da965337574.png)

```java
@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "見つかりません")
```
ステータスコードと理由を付けることができるという話です。

---

> ステータスコードはハンドラーメソッドが呼び出されるときにHTTP応答に適用されますが、{@code ResponseEntity}や{@code "redirect:"}のような他の方法で設定されたステータス情報を上書きすることはありません。

@ResponseStatusよりResponseEntityを使用したステータス情報が優先的に適用されるという意味です。
本当にそうなのか、一度確認してみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/acaece6b-be07-10f6-99f0-bca96e4dd77d.png)

```java
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @PostMapping()
    public ResponseEntity<String> test(){
        return ResponseEntity.ok("hello");
    }
```

以下の写真のようなコードを作成し、呼び出してみました。（便宜のためにswaggerを使用しました。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/e34e1aac-b9bb-ba5d-11f0-0637e025d090.png)

helloが正常に返されることが分かります。
なぜなら、ResponseEntityがメソッド内部で明示的に応答を設定するため、@ ResponseStatusよりも優先的に適用されるからです。ResponseEntityに関する内容は後で続けて扱います。

---

> 警告: このアノテーションを例外クラスに使用したり、このアノテーションの{@code reason}属性を設定する際には、{@code HttpServletResponse.sendError}メソッドが使用されます。

先ほど最初に扱った内容の延長線上です。
@ResponseEntityを例外クラスに付けると、その例外が発生したときに特定のHTTPステータスコードとメッセージが応答として送信されます。
また、reasonにステータスコードと共に理由を含めることができます。
この場合、Spring内部でHttpServletResponse.sendErrorを使用してステータスコードとメッセージが伝達されます。


```java
/**
     * Sends an error response to the client using the specified status code and clears the output buffer. The server
     * defaults to creating the response to look like an HTML-formatted server error page containing the specified
     * message, setting the content type to "text/html", leaving cookies and other headers unmodified. If an error-page
     * declaration has been made for the web application corresponding to the status code passed in, it will be served
     * back in preference to the suggested msg parameter.
     * <p>
     * If the response has already been committed, this method throws an IllegalStateException. After using this method,
     * the response should be considered to be committed and should not be written to.
     *
     * @param sc  the error status code
     * @param msg the descriptive message
     *
     * @exception IOException           If an input or output exception occurs
     * @exception IllegalStateException If the response was committed
     */
    void sendError(int sc, String msg) throws IOException;

    /**
     * Sends an error response to the client using the specified status code and clears the buffer. This is equivalent
     * to calling {@link #sendError(int, String)} with the same status code and <code>null</code> for the message.
     *
     * @param sc the error status code
     *
     * @exception IOException           If an input or output exception occurs
     * @exception IllegalStateException If the response was committed before this method call
     */
    void sendError(int sc) throws IOException;
```

HttpServletResponse.sendErrorに入ると、次のような内容を見ることができます。

```java
void sendError(int sc, String msg) throws IOException;
```

- これを通じて、ステータスコードとメッセージを使用してクライアントにエラーを送信します。（メッセージは省略されることがあります）
- 両方のメソッドは出力バッファをクリアし、応答がコミットされた状態ではIllegalStateExceptionを発生させます

- ステータスコードに対するエラーページの宣言がある場合、そのエラーページが返されます

- 応答は完了したと見なされるため、それ以降はもう応答を作成できません

- これに関連する内容が続きますので、引き続き説明します

---

> {@code HttpServletResponse.sendError}を使用する場合、応答は完了したものと見なされ、それ以上作成されるべきではありません。また、サーブレットコンテナは通常、HTMLエラーページを作成するため、{@code reason}の使用はREST APIには適していません。このような場合、{@code @ResponseStatus}を使用せず、戻り値の型として{@link org.springframework.http.ResponseEntity}を使用することをお勧めします。

前述のコメントでも述べたように、サーブレットコンテナは通常HTMLエラーページを生成します。

これはREST API形式とは合いません。
なぜなら、通常REST APIはJSON形式の応答を使用するからです。

そのため、このような場合は **@ ResponseStatusの代わりにResponseEntityを使用することが推奨されています**。


ResponseEntityを使用することで、ステータスコードと応答本文を明示的に設定でき、JSONにエラーメッセージを含めるなど、よりREST APIに適した応答が可能だからです。

```java
@GetMapping("/resource/{id}")
public ResponseEntity<String> getResource(@PathVariable String id) {
    Resource resource = findResourceById(id);
    if (resource == null) {
        // JSON形式のエラーメッセージと共に404ステータスコードが返されます。
        return ResponseEntity.status(HttpStatus.NOT_FOUND)
                             .body("{\"error\": \"Resource not found\"}");
    }

```
- ResponseEntity使用例

---

## ResponseEntityについて調べる

それでは、ResponseEntityはどのようなものでしょうか？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3815412/9a5e0ea2-ea7c-25b4-fe87-db32cf43e15d.png)


> {@link HttpEntity}を拡張して、{@link HttpStatusCode}ステータスコードを追加します。
>- {@code RestTemplate}および{@code @Controller}メソッドで使用されます

HttpEntityを拡張してHTTPステータスコードを追加したクラスです。
SpringのRestTemplateと@Controllerで使用されます。

```java
{@code RestTemplate}では、このクラスが
{@link org.springframework.web.client.RestTemplate#getForEntity getForEntity()}および
{@link org.springframework.web.client.RestTemplate#exchange exchange()}によって返されます。
```

続く説明です。getForEntityとexchangeメソッドがResponseEntityオブジェクトを返すと言っています。

```java
* // getForEntity 例
 * ResponseEntity&lt;String&gt; entity = template.getForEntity("https://example.com", String.class);
 * String body = entity.getBody();
 * MediaType contentType = entity.getHeaders().getContentType();
 * HttpStatus statusCode = entity.getStatusCode();
 *
 * // exchange 例
 * ResponseEntity&lt;String&gt; exchangeEntity = template.exchange("https://example.com", HttpMethod.GET, null, String.class);
 * String exchangeBody = exchangeEntity.getBody();
 * MediaType exchangeContentType = exchangeEntity.getHeaders().getContentType();
 * HttpStatus exchangeStatusCode = exchangeEntity.getStatusCode();
 * </pre>

```

---

> このクラスはまた、Spring MVCで{@code @Controller}メソッドの戻り値として使用することもできます。

```java
@RequestMapping("/handle")
public ResponseEntity<String> handle() {
    URI location = ...;
    HttpHeaders responseHeaders = new HttpHeaders();
    responseHeaders.setLocation(location);
    responseHeaders.set("MyResponseHeader", "MyValue");
    return new ResponseEntity<String>("Hello World", responseHeaders,
HttpStatus.CREATED);
}
```
ResponseEntityを戻り値として使用できるという内容です。

---

>または、静的メソッドを通じてアクセス可能なビルダーを使用します。

```java
@RequestMapping("/handle")
public ResponseEntity<String> handle() {
    URI location = ...;
    return ResponseEntity.created(location)
                         .header("MyResponseHeader", "MyValue")
                         .body("Hello World");
}
```

このようにビルダーパターンを通じて使用することもできます。

---

## 終わりに

:::note info
結論
Rest APIの環境では、ResponseEntityを使用することが適切です。
:::

また、ResponseEntityを使うと

```java
@PostMapping()
public ResponseEntity<String> createOrUpdateResource(@RequestBody Resource resource) {
 boolean exists = resourceService.exists(resource.getId());
  
  if (exists) {
      resourceService.update(resource);
      return ResponseEntity.ok("update success!");
  } else {
      resourceService.create(resource);
      return ResponseEntity.status(HttpStatus.CREATED).body("create success!");
  }
}
```

 このように、応答コードをはるかに柔軟に設定できます。


やはりResponseEntityを使うのが正しいというのが…私の結論です。