1. 概要

この記事では、SpringでHttpMessageConvertersを構成する方法について説明します。

簡単に言うと、メッセージコンバーターを使用して、HTTPを介してJSON、XMLなどとの間でJavaオブジェクトをマーシャリングおよびアンマーシャリングできます。

参考文献:

SpringMVCコンテンツネゴシエーション

Spring MVCアプリケーションでコンテンツネゴシエーションを構成し、利用可能なさまざまな戦略を有効または無効にするためのガイド。

SpringMVCで画像/メディアデータを返す

この記事では、Spring MVCで画像(または他のメディア)を返すための代替案を示し、各アプローチの長所と短所について説明します。

SpringRESTAPIのバイナリデータ形式

この記事では、Kryoで説明するバイナリデータ形式を利用するようにSpringRESTメカニズムを構成する方法について説明します。 さらに、Googleプロトコルバッファで複数のデータ形式をサポートする方法を示します。

2. 基礎

2.1. WebMVCを有効にする

まず、WebアプリケーションをSpringMVCサポートで構成する必要があります。これを行うための便利で非常にカスタマイズ可能な方法は、@EnableWebMvcアノテーションを使用することです。

@EnableWebMvc
@Configuration
@ComponentScan({ "com.baeldung.web" })
public class WebConfig implements WebMvcConfigurer {
    
    // ...
    
}

このクラスはWebMvcConfigurerを実装していることに注意してください。これにより、HttpConvertersのデフォルトリストを独自のものに変更できます。

2.2. デフォルトのメッセージコンバータ

デフォルトでは、次の HttpMessageConverter■インスタンスは事前に有効になっています。

  • ByteArrayHttpMessageConverter –バイト配列を変換します
  • StringHttpMessageConverter –文字列を変換します
  • ResourceHttpMessageConverterorg.springframework.core.io.Resourceを任意のタイプのオクテットストリームに変換します
  • SourceHttpMessageConverterjavax.xml.transform.Sourceを変換します
  • FormHttpMessageConverter –フォームデータを MultiValueMap
  • Jaxb2RootElementHttpMessageConverter – JavaオブジェクトをXMLとの間で変換します(クラスパスにJAXB2が存在する場合にのみ追加されます)
  • MappingJackson2HttpMessageConverter – JSONを変換します(Jackson 2がクラスパスに存在する場合にのみ追加されます) '
  • MappingJacksonHttpMessageConverter – JSONを変換します(Jacksonがクラスパスに存在する場合にのみ追加されます)
  • AtomFeedHttpMessageConverter – Atomフィードを変換します(ローマがクラスパスに存在する場合にのみ追加されます)
  • RssChannelHttpMessageConverter – RSSフィードを変換します(ローマがクラスパスに存在する場合にのみ追加されます)

3. クライアント/サーバー通信–JSONのみ

3.1. 高レベルのコンテンツネゴシエーション

HttpMessageConverter実装には、1つまたは複数の関連付けられたMIMEタイプがあります。

新しいリクエストを受信すると、 Springは「Accept」ヘッダーを使用して、で応答する必要のあるメディアタイプを決定します。

次に、その特定のメディアタイプを処理できる登録済みのコンバーターを見つけようとします。 最後に、これを使用してエンティティを変換し、応答を送り返します。

このプロセスは、JSON情報を含むリクエストを受信する場合と同様です。 フレームワークはを使用しますコンテンツタイプ」ヘッダーは、リクエスト本文のメディアタイプを決定します

次に、 HttpMessageConverter クライアントから送信された本文をJavaオブジェクトに変換できます。

簡単な例でこれを明確にしましょう:

  • クライアントは、Acceptヘッダーをapplication/jsonに設定して/foos にGETリクエストを送信し、すべてのFooリソースをJSONとして取得します
  • Foo Spring Controllerがヒットし、対応する FooJavaエンティティを返します
  • 次に、SpringはJacksonメッセージコンバーターの1つを使用して、エンティティをJSONにマーシャリングします

次に、'で、これがどのように機能するか、および@ResponseBodyおよび@RequestBodyアノテーションを活用する方法の詳細を見てみましょう。

3.2. @ResponseBody

Controllerメソッドの@ResponseBodyは、メソッドの戻り値がHTTPResponseの本体に直接シリアル化されることをSpringに示します。 上で説明したように、クライアントによって指定された「 Accept 」ヘッダーは、エンティティをマーシャリングするための適切なHttpConverterを選択するために使用されます。

'に簡単な例を見てみましょう

@GetMapping("/{id}")
public @ResponseBody Foo findById(@PathVariable long id) {
    return fooService.findById(id);
}

これで、クライアントはリクエストの application / json に「Accept」ヘッダーを指定します–例 curlコマンド:

curl --header "Accept: application/json" 
  http://localhost:8080/spring-boot-rest/foos/1

Foo クラス:

public class Foo {
    private long id;
    private String name;
}

そして、HTTP応答本文:

{
    "id": 1,
    "name": "Paul",
}

3.3. @RequestBody

使用できます @RequestBody 注釈コントローラメソッドの引数で、HTTPリクエストの本文がその特定のJavaエンティティに逆シリアル化されていることを示します。 適切なコンバーターを決定するために、Springはクライアント要求からの「Content-Type」ヘッダーを使用します。

例を見てみましょう:

@PutMapping("/{id}")
public @ResponseBody void update(@RequestBody Foo foo, @PathVariable String id) {
    fooService.update(foo);
}

次に、'がこれをJSONオブジェクトで使用します。'「Content-Typeapplication /jsonに指定します。

curl -i -X PUT -H "Content-Type: application/json"  
-d '{"id":"83","name":"klik"}' http://localhost:8080/spring-boot-rest/foos/1

200 OKが返されます–成功した応答:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Length: 0
Date: Fri, 10 Jan 2014 11:18:54 GMT

4. カスタムコンバーターの構成

WebMvcConfigurerインターフェイスを実装し、 configureMessageConverters メソッドをオーバーライドすることで、メッセージコンバーターをカスタマイズすることもできます。

@EnableWebMvc
@Configuration
@ComponentScan({ "com.baeldung.web" })
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
        messageConverters.add(createXmlHttpMessageConverter());
        messageConverters.add(new MappingJackson2HttpMessageConverter());
    }

    private HttpMessageConverter<Object> createXmlHttpMessageConverter() {
        MarshallingHttpMessageConverter xmlConverter = new MarshallingHttpMessageConverter();

        XStreamMarshaller xstreamMarshaller = new XStreamMarshaller();
        xmlConverter.setMarshaller(xstreamMarshaller);
        xmlConverter.setUnmarshaller(xstreamMarshaller);

        return xmlConverter;
    } 
}

この例では、'新しいコンバーター[ MarshallingHttpMessageConverter )を作成し、SpringXStreamサポートを使用して構成します。 we 'は基盤となるマーシャリングフレームワーク(この場合はXStream)の低レベルAPIを使用しているため、これにより大きな柔軟性が得られ、必要に応じて構成できます。

この例では、XStreamライブラリをクラスパスに追加する必要があることに注意してください。

また、このサポートクラスを拡張することにより、we'は以前に事前登録されていたデフォルトのメッセージコンバーターを失うことに注意してください。

もちろん、ジャクソンについても同じことができます。独自の MappingJackson2HttpMessageConverter。を定義することで、このコンバーターにカスタム ObjectMapper を設定し、必要に応じて構成することができます。

この場合、XStreamが選択されたマーシャラー/アンマーシャラーの実装でしたが、JibxMarshallerのようなothersも使用できます。

この時点で(バックエンドでXMLが有効になっている場合)、XML表現を使用してAPIを使用できます。

curl --header "Accept: application/xml" 
  http://localhost:8080/spring-boot-rest/foos/1

4.1. スプリングブートサポート

' Spring Bootを使用している場合は、 WebMvcConfigurer を実装したり、上記のようにすべてのメッセージコンバーターを手動で追加したりすることを回避できます。

コンテキストでさまざまなHttpMessageConverter Beanを定義するだけで、SpringBootはそれらを作成する自動構成に自動的に追加します。

@Bean
public HttpMessageConverter<Object> createXmlHttpMessageConverter() {
    MarshallingHttpMessageConverter xmlConverter = new MarshallingHttpMessageConverter();

    // ...

    return xmlConverter;
}

5. Using Spring’s RestTemplate With HTTP Message Converters

サーバー側と同様に、HTTPメッセージ変換はSpring RestTemplateのクライアント側で構成できます。

必要に応じて、「Accept」および「Content-Type」ヘッダーを使用してテンプレートを構成します。 次に、'は、 Foo リソースの完全なマーシャリングとアンマーシャリング(JSONとXMLの両方)を使用してRESTAPIを使用しようとします。

5.1. Retrieving the Resource With No Accept Header

@Test
public void whenRetrievingAFoo_thenCorrect() {
    String URI = BASE_URI + "foos/{id}";

    RestTemplate restTemplate = new RestTemplate();
    Foo resource = restTemplate.getForObject(URI, Foo.class, "1");

    assertThat(resource, notNullValue());
}

5.2. Retrieving a Resource With application/xml Accept Header

ここで、リソースをXML表現として明示的に取得してみましょう。 'コンバーターのセットを定義し、これらをRestTemplateに設定します。

XMLを使用しているため、以前と同じXStreamマーシャラーを使用します。

@Test
public void givenConsumingXml_whenReadingTheFoo_thenCorrect() {
    String URI = BASE_URI + "foos/{id}";

    RestTemplate restTemplate = new RestTemplate();
    restTemplate.setMessageConverters(getXmlMessageConverters());

    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(Collections.singletonList(MediaType.APPLICATION_XML));
    HttpEntity<String> entity = new HttpEntity<>(headers);

    ResponseEntity<Foo> response = 
      restTemplate.exchange(URI, HttpMethod.GET, entity, Foo.class, "1");
    Foo resource = response.getBody();

    assertThat(resource, notNullValue());
}

private List<HttpMessageConverter<?>> getXmlMessageConverters() {
    XStreamMarshaller marshaller = new XStreamMarshaller();
    marshaller.setAnnotatedClasses(Foo.class);
    MarshallingHttpMessageConverter marshallingConverter = 
      new MarshallingHttpMessageConverter(marshaller);

    List<HttpMessageConverter<?>> converters = new ArrayList<>();
    converters.add(marshallingConverter);
    return converters;
}

5.3. Retrieving a Resource With application/json Accept Header

同様に、JSONを要求してRESTAPIを使用してみましょう。

@Test
public void givenConsumingJson_whenReadingTheFoo_thenCorrect() {
    String URI = BASE_URI + "foos/{id}";

    RestTemplate restTemplate = new RestTemplate();
    restTemplate.setMessageConverters(getJsonMessageConverters());

    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
    HttpEntity<String> entity = new HttpEntity<String>(headers);

    ResponseEntity<Foo> response = 
      restTemplate.exchange(URI, HttpMethod.GET, entity, Foo.class, "1");
    Foo resource = response.getBody();

    assertThat(resource, notNullValue());
}

private List<HttpMessageConverter<?>> getJsonMessageConverters() {
    List<HttpMessageConverter<?>> converters = new ArrayList<>();
    converters.add(new MappingJackson2HttpMessageConverter());
    return converters;
}

5.4. Update a Resource With XML Content-Type

最後に、let'もJSONデータをRESTAPIに送信し、Content-Typeヘッダーを介してそのデータのメディアタイプを指定します。

@Test
public void givenConsumingXml_whenWritingTheFoo_thenCorrect() {
    String URI = BASE_URI + "foos";
    RestTemplate restTemplate = new RestTemplate();
    restTemplate.setMessageConverters(getJsonAndXmlMessageConverters());

    Foo resource = new Foo("jason");
    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
    headers.setContentType((MediaType.APPLICATION_XML));
    HttpEntity<Foo> entity = new HttpEntity<>(resource, headers);

    ResponseEntity<Foo> response = 
      restTemplate.exchange(URI, HttpMethod.POST, entity, Foo.class);
    Foo fooResponse = response.getBody();

    assertThat(fooResponse, notNullValue());
    assertEquals(resource.getName(), fooResponse.getName());
}

private List<HttpMessageConverter<?>> getJsonAndXmlMessageConverters() {
    List<HttpMessageConverter<?>> converters = getJsonMessageConverters();
    converters.addAll(getXmlMessageConverters());
    return converters;
}

ここで'興味深いのは、'メディアタイプを混在させることができることです。私たちは' XMLデータを送信していますが、'サーバーからのJSONデータを待っています。 これは、Spring変換メカニズムが実際にどれほど強力であるかを示しています。

6. 結論

このチュートリアルでは、Spring MVCを使用して、XMLまたはJSONとの間でJavaエンティティを自動的にマーシャル/アンマーシャルするへのHttpメッセージコンバーターを指定して完全にカスタマイズする方法について説明しました。 もちろん、これは単純な定義であり、最後のテスト例からわかるように、メッセージ変換メカニズムで実行できることは他にもたくさんあります。

また、 RestTemplate クライアントで同じ強力なメカニズムを活用する方法についても見てきました。これにより、APIを完全にタイプセーフに使用できるようになります。

いつものように、この記事で紹介するコードは、GitHubから入手できます。

一般的なフッターバナー