Springフレームワークを使ったHTTPメッセージコンバータ

Spring Frameworkを使用したHTTPメッセージコンバーター

1. 概要

この記事では、how to Configure HttpMessageConverters in Springについて説明します。

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

参考文献:

Spring MVCコンテンツネゴシエーション

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

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

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

Spring REST APIのバイナリデータ形式

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

2. 基礎

2.1. WebMVCを有効にする

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

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

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

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

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

  • ByteArrayHttpMessageConverter –バイト配列を変換します

  • StringHttpMessageConverter –文字列を変換します

  • ResourceHttpMessageConverter –任意のタイプのオクテットストリームのorg.springframework.core.io.Resourceを変換します

  • SourceHttpMessageConverterjavax.xml.transform.Sourceを変換します

  • FormHttpMessageConverter –フォームデータをMultiValueMap<String, String>との間で変換します。

  • Jaxb2RootElementHttpMessageConverter – JavaオブジェクトをXMLとの間で変換します(JAXB2がクラスパスに存在する場合にのみ追加されます)

  • MappingJackson2HttpMessageConverter – JSONを変換します(Jackson 2がクラスパスに存在する場合にのみ追加されます)__

  • MappingJacksonHttpMessageConverter – JSONを変換します(Jacksonがクラスパスに存在する場合にのみ追加されます)

  • AtomFeedHttpMessageConverter – Atomフィードを変換します(ローマがクラスパスに存在する場合にのみ追加されます)

  • RssChannelHttpMessageConverter – RSSフィードを変換します(added only if Rome is present on the classpath)

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

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

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

新しいリクエストを受信すると、Spring will use the “Accept” header to determine the media type that it needs to respond withになります。

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

このプロセスは、JSON情報を含むリクエストを受信する場合と似ています。 フレームワークは、リクエスト本文のメディアタイプを決定するために「use the “*Content-Type」ヘッダーを作成します*。

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

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

  • クライアントは、Acceptヘッダーをapplication/jsonに設定して/foosにGETリクエストを送信します–すべてのFooリソースをJSONとして取得します

  • Foo Spring Controllerがヒットし、対応するFooJavaエンティティーを返します

  • その後、SpringはJacksonメッセージコンバーターのいずれかを使用して、エンティティをJSONにマーシャリングします

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

3.2. @ResponseBody

Controllerメソッドの@ResponseBodyは、Springにthe return value of the method is serialized directly to the body of the HTTP Responseを示します。 上で説明したように、クライアントによって指定された「Accept」ヘッダーは、エンティティをマーシャリングするための適切なHttpコンバーターを選択するために使用されます。

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

@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

Controllerメソッドの引数に@RequestBodyアノテーションを使用して、that the body of the HTTP Request is deserialized to that particular Java entityを示すことができます。 適切なコンバーターを決定するために、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. カスタムコンバーターの構成

customize the message converters by implementing the WebMvcConfigurer interfaceを使用して、configureMessageConvertersメソッドをオーバーライドすることもできます。

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

    @Override
    public void configureMessageConverters(
      List> converters) {

        messageConverters.add(createXmlHttpMessageConverter());
        messageConverters.add(new MappingJackson2HttpMessageConverter());
    }
    private HttpMessageConverter createXmlHttpMessageConverter() {
        MarshallingHttpMessageConverter xmlConverter =
          new MarshallingHttpMessageConverter();

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

        return xmlConverter;
    }
}


対応するXML構成は次のとおりです。



    
        
        
            
            
        
    


この例では、新しいコンバーター(MarshallingHttpMessageConverter)を作成し、SpringXStreamサポートを使用して構成します。 これにより、we’re working with the low-level APIs of the underlying marshalling framework(この場合はXStream)が非常に柔軟になり、必要に応じて構成できます。

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

このサポートクラスを拡張することにより、we’re losing the default message converters which were previously pre-registered.にも注意してください。

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

この場合、XStreamが選択されたマーシャラー/アンマーシャラーの実装でしたが、CastorMarshallerのような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 createXmlHttpMessageConverter() {
    MarshallingHttpMessageConverter xmlConverter = new MarshallingHttpMessageConverter();

    // ...

    return xmlConverter;
}





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

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

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

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

@Test
public void testGetFoo() {
    String URI = “http://localhost:8080/spring-boot-rest/foos/{id}";
    RestTemplate restTemplate = new RestTemplate();
    Foo foo = restTemplate.getForObject(URI, Foo.class, "1");
    Assert.assertEquals(new Integer(1), foo.getId());
}

5.2. application/xmlAcceptヘッダーを使用してリソースを取得する

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

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

@Test
public void givenConsumingXml_whenReadingTheFoo_thenCorrect() {
    String URI = BASE_URI + "foos/{id}";
    RestTemplate restTemplate = new RestTemplate();
    restTemplate.setMessageConverters(getMessageConverters());

    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(Arrays.asList(MediaType.APPLICATION_XML));
    HttpEntity entity = new HttpEntity(headers);

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

    assertThat(resource, notNullValue());
}
private List> getMessageConverters() {
    XStreamMarshaller marshaller = new XStreamMarshaller();
    MarshallingHttpMessageConverter marshallingConverter =
      new MarshallingHttpMessageConverter(marshaller);

    List> converters =
      ArrayList>();
    converters.add(marshallingConverter);
    return converters;
}

5.3. application/jsonAcceptヘッダーを使用してリソースを取得する

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

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

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

    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
    HttpEntity entity = new HttpEntity(headers);

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

    assertThat(resource, notNullValue());
}
private List> getMessageConverters() {
    List> converters =
      new ArrayList>();
    converters.add(new MappingJackson2HttpMessageConverter());
    return converters;
}

5.4. XMLContent-Typeでリソースを更新する

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

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

    Foo resource = new Foo(4, "jason");
    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
    headers.setContentType((MediaType.APPLICATION_XML));
    HttpEntity entity = new HttpEntity(resource, headers);

    ResponseEntity response = restTemplate.exchange(
      URI, HttpMethod.PUT, entity, Foo.class, resource.getId());
    Foo fooResponse = response.getBody();

    Assert.assertEquals(resource.getId(), fooResponse.getId());
}

ここで興味深いのは、メディアタイプ(we’re sending XML data but we’re waiting for JSON data back from the server)を混在させることができることです。 これは、Spring変換メカニズムが実際にどれだけ強力かを示しています。

6. 結論

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

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

いつものように、この記事で紹介されているコードはover on Githubで利用できます。