1. 概要

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

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

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 –文字列を変換します
  • ResourceHttpMessageConverter org.springframework.core.io.Resourceを任意のタイプのオクテットストリームに変換します
  • SourceHttpMessageConverter javax.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サポートを使用して構成します。 これにより、基盤となるマーシャリングフレームワーク(この場合はXStream)の低レベルAPIを使用しているため、柔軟性が大幅に向上し、必要に応じて構成できます。

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

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

もちろん、ジャクソンについても同じことができます。独自の 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サポート

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

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

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

    // ...

    return xmlConverter;
}

5. SpringのRestTemplateをHTTPメッセージコンバーターで使用する

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

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

5.1. Acceptヘッダーのないリソースの取得

@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. application / xml Acceptヘッダーを使用したリソースの取得

ここで、リソースを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. application / json Acceptヘッダーを使用したリソースの取得

同様に、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. XMLでリソースを更新するContent-Type

最後に、JSONデータをREST APIに送信し、 Content-Typeheaderを介してそのデータのメディアタイプを指定しましょう。

@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から入手できます。