1. 序章

コンテンツタイプは、要求/応答に存在するデータを解釈する方法を示します。 コントローラがWeb要求を受信するたびに、いくつかのメディアタイプを消費または生成します。 この要求/応答モデルでは、いくつかのメディアタイプを消費/生成でき、JSONはその1つです。

このクイックチュートリアルでは、SpringMVCでコンテンツタイプを設定するさまざまな方法を見ていきます。

2. Springの@RequestMapping

簡単に言うと、 @RequestMapping は、WebリクエストをSpringコントローラーにマップする重要なアノテーションです。 HTTPメソッド、リクエストパラメータ、ヘッダー、メディアタイプなどのさまざまな属性があります。

一般に、メディアの種類は、消耗品と生産性の2つのカテゴリに分類されます。 これら2つ以外に、Springカスタムメディアタイプを定義することもできます。 主な目的は、プライマリマッピングを、リクエストハンドラーのメディアタイプのリストに制限することです。

2.1. 消費可能なメディアタイプ

属性を消費することで、コントローラーがクライアントから受け入れるメディアタイプを指定します。 メディアタイプのリストも提供できます。 簡単なエンドポイントを定義しましょう:

@RequestMapping(value = "/greetings", method = RequestMethod.POST, consumes="application/json")
public void addGreeting(@RequestBody ContentType type, Model model) {
    // code here
}

クライアントがリソースで消費できないメディアタイプを指定した場合、システムはHTTP「415UnsupportedMediaType」エラーを生成します。

2.2. 生産可能なメディアタイプ

consumes 属性とは対照的に、 products は、リソースが生成してクライアントに送り返すことができるメディアタイプを指定します。 間違いなく、オプションのリストを使用できます。 リソースが要求されたリソースを生成できない場合、システムはHTTP「406NotAcceptable」エラーを生成します。

簡単な例から始めましょう–JSON文字列を公開するAPI。

エンドポイントは次のとおりです。

@RequestMapping(
  value = "/greetings-with-response-body", 
  method = RequestMethod.GET, 
  produces="application/json"
) 
@ResponseBody
public String getGreetingWhileReturnTypeIsString() { 
    return "{\"test\": \"Hello using @ResponseBody\"}";
}

CURLを使用してこれをテストしてみましょう。

curl http://localhost:8080/greetings-with-response-body

上記のコマンドは、次の応答を生成します。

{ "test": "Hello using @ResponseBody" }

ヘッダーに存在するコンテンツタイプに基づいて、@ResponseBodyはメソッドの戻り値のみをWeb応答本文にバインドします。

3. コンテンツタイプが適切に設定されていない

メソッドの戻りタイプがString、であり、クラスパスにJSONマッパーが存在しない場合。 この場合、戻り値は、コンテンツタイプを「text/plain」に設定するStringHttpMessageConverterクラスによって処理されます。 これにより、コントローラーが期待されるコンテンツタイプを生成できないという問題が発生することがよくあります。

この問題を解決するためのさまざまなアプローチを見てみましょう。

3.1. JSONマッパーで@ResponseBodyを使用する

Jackson ObjectMapper クラスは、文字列、ストリーム、またはファイルからJSONを解析します。 Jacksonがクラスパス上にある場合、SpringアプリケーションのコントローラーはデフォルトでJSON応答をレンダリングします。

クラスパスにJacksonを含めるには、pom.xmlに次の依存関係を追加する必要があります。

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.4</version>
</dependency>

応答から検証する単体テストを追加しましょう。

@Test
public void givenReturnTypeIsString_whenJacksonOnClasspath_thenDefaultContentTypeIsJSON() 
  throws Exception {
    
    // Given
    String expectedMimeType = "application/json";
    
    // Then
    String actualMimeType = this.mockMvc.perform(MockMvcRequestBuilders.get("/greetings-with-response-body", 1))
      .andReturn().getResponse().getContentType();

    Assert.assertEquals(expectedMimeType, actualMimeType);
}

3.2. ResponseEntiyを使用する

@ResponseBody とは対照的に、 ResponseEntity は、HTTP応答全体を表す汎用タイプです。 その結果、ステータスコード、ヘッダー、本文など、それに入るすべてのものを制御できます。

新しいエンドポイントを定義しましょう:

@RequestMapping(
  value = "/greetings-with-response-entity",
  method = RequestMethod.GET, 
  produces = "application/json"
)
public ResponseEntity<String> getGreetingWithResponseEntity() {
    final HttpHeaders httpHeaders= new HttpHeaders();
    httpHeaders.setContentType(MediaType.APPLICATION_JSON);
    return new ResponseEntity<String>("{\"test\": \"Hello with ResponseEntity\"}", httpHeaders, HttpStatus.OK);
}

ブラウザの開発者コンソールで、次の応答を確認できます。

{"test": "Hello with ResponseEntity"}

ResponseEntity を使用すると、ディスパッチャーサーブレットにアノテーション駆動型タグが含まれるはずです。

<mvc:annotation-driven />

簡単に言えば、上記のタグを使用すると、 SpringMVCの内部動作をより細かく制御できます。

テストケースを使用して、応答のコンテンツタイプを確認しましょう。

@Test
public void givenReturnTypeIsResponseEntity_thenDefaultContentTypeIsJSON() throws Exception {
    
    // Given
    String expectedMimeType = "application/json";
    
    // Then
    String actualMimeType = this.mockMvc.perform(MockMvcRequestBuilders.get("/greetings-with-response-entity", 1))
      .andReturn().getResponse().getContentType();

    Assert.assertEquals(expectedMimeType, actualMimeType);
}

3.3. 使用する地図返品タイプ

最後になりましたが、リターンタイプをStringからMapに変更することで、コンテンツタイプを設定することもできます。 このMap戻りタイプはマーシャリングが必要であり、JSONオブジェクトを返します。

新しいエンドポイントは次のとおりです。

@RequestMapping(
  value = "/greetings-with-map-return-type", 
  method = RequestMethod.GET, 
  produces = "application/json"
)
@ResponseBody
public Map<String, Object> getGreetingWhileReturnTypeIsMap() {
    HashMap<String, Object> map = new HashMap<String, Object>();
    map.put("test", "Hello from map");
    return map;
}

これを実際に見てみましょう:

curl http://localhost:8080/greetings-with-map-return-type

curlコマンドはJSON応答を返します。

{ "test": "Hello from map" }

4. 結論

この記事では、Spring MVCでコンテンツタイプを設定する方法について説明します。最初にクラスパスにJsonマッパーを追加し、次にResponseEntityを使用して、最後に戻りタイプをStringからMapに変更します。

いつものように、すべてのコードスニペットはGitHubにあります。