8.Springが用意するWEBクライアント(RestTemplate)の使い方

概要

Springでは外部のWEBと通信するための機構を持っています。
RestTemplateと呼ばれる機能で、以下のことが可能です。
・Jsonの送信
・XMLの送信
・リクエストパラメタの送信
・マルチパート(ファイルアップロード)の送信
・Jsonの受信
・XMLの受信
・ファイルダウンロード
・HTML等の受信
かなり便利で、使い方も簡単です。覚えておいて損はないと思います。
ここでは、RestTemplateの仕組みの概要と、使い方の例をTips的に示したいと思います。 
例を見ればすぐに使えますが、軽く仕組みもお読みいただけると理解しやすいかと思います。 
 
【バージョンについて】
この記事のSpringバージョンは以下の通りです。
 ・Spring 4.3.4以上です。
 
 
 
 
 
 
 

RestTemplateの仕組み

 
 
重要な処理だけに注目した場合、RestTemplateの大まかな仕組みは上記のようになります。
プログラマは、リクエストオブジェクトを渡すと、レスポンスオブジェクトを受け取れます。
覚えておくべきは、MessageConverter(ボディ変換器)がリクエスト時、レスポンス時のボディ部の変換を行うことです。
 
【リクエスト処理】 
<ボディ部>
ボディ変換器(MessageConverter)はRestTemplate内に複数存在し、条件がマッチするものが適用されます。
条件のマッチは、リクエスト(RequestEntity)のContent-Typeと送信オブジェクトの型で判断されます。
例えば、リクエストオブジェクトのContent-TypeがJsonのとき、Jsonのボディ変換器が処理をするので、
送信オブジェクト(POJO)をJsonに変換し、ボディ部に設定してサーバーに送ります。
ボディ部がResourceオブジェクトのときは、Resourceのボディ変換器が処理をするので、
Resourceで指定したファイルの内容をボディ部に設定してサーバーに送ります。
 
<リクエスト時の文字コード>
サーバーに送るボディ部の文字コードは、基本的にはリクエストのContent-Typeを読み込んで決められます。
ただ、ボディ変換器によって許される文字コードが決まっているので、例えばJsonの変換器の場合、UTF-8系の文字コードしか
許されないため、Shift_Jisなどを指定しても無視されます。
 
【レスポンス処理】
<ボディ部>
レスポンスの受け取りは、ResponseEntityにHTTPヘッダとボディ部の値が設定されます。
プログラマは、ResponseEntityを操作することで受け取った値を知ることができます。
<レスポンス時の文字コード>
レスポンス時のボディ部の文字コードは、デフォルトではUTF-8です。
文字コード変換は、ボディ変換器のStringHttpMessageConverterが担っています。
ですので、文字コードを変更したい場合はこのボディ変換器を操作することになります。後で例を示します。
 
【内部クライアント】
RestTemplateの内部クライアントとして、Java標準のHttpURLConnection、Apache HttpClientなどを指定できます。
違いはそれぞれのフレームワークの仕様等を確認するのが良いと思います。
軽く違いを記述すると、HttpURLConnectionはリダイレクトやセッション管理の機能はありませんが、
Apache HttpClientにはあったはずです。処理速度も違うので、用途に応じて使い分ければよいかと思います。
 
【注意点】
RestTemplateのレスポンス処理は、HTTPステータスが500などのエラーの場合、例外を発生します。
もし、ステータスによって処理を変える場合は、try~catchで例外を受け取るかエラーハンドリングを使用すると良いと思います。
 
 
 
 

RestTemplateの使い方Tips 

リクエストオブジェクトの作り方いろいろ

ここでは、リクエストオブジェクト(RequestEntity)の作成方法を見ていきます。
リクエストオブジェクトはこのあと見ていく、実際の外部との通信で使用します。
作成方法は以下のいずれでも、好きな方法で作成してください。

HTTPヘッダを細かく設定できる方法

URI uri = new URI("http://localhost/test");
//HTTPヘッダ作成
HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType("text", "json", Charset.forName("utf-8")));
 
//リクエスト作成(sjsonは自作のPOJO)
JsonSendObjSample sjson = new JsonSendObjSample();
RequestEntity<?> req = new RequestEntity<>(sjson, headers, HttpMethod.POST, uri);
 
 ※sjsonは自作POJO
 この方法では、HTTPヘッダはどんなものでも自由に設定できます。

HTTPヘッダを指定して簡易に作成する方法

RequestEntity<?> req = RequestEntity.post(new URI("http://localhost/test"))
    .contentType(MediaType.APPLICATION_JSON_UTF8)
    .body(sjson);
 ※sjsonは自作POJO
 この方法では、Content-Typeなど一部のHTTPヘッダしか設定できませんが、少ないステップ数で記述ができます。
 

WEBとの通信のやり方いろいろ

クライアント(RestTemplate)の作成方法

通信は以下のようにクライアント(RestTemplate)を作成して行います。
ここでは、以下のようにclientを生成している前提でTipsを書いていきます。
【クライアント(RestTemplate)の作成】
 
URI uri = new URI("http://localhost/test");
RestTemplate client = new RestTemplate(new SimpleClientHttpRequestFactory());
 
 SimpleClientHttpRequestFactoryはHttpURLConnectionを利用する内部クライアントを生成するファクトリーです。
 Apache HTTP Clientを利用する内部クライアントなど他のファクトリを指定することもできます。
 

Jsonのリクエスト 

//ヘッダ作成
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
 
 
//リクエスト作成(JsonSendObjSampleは自作POJO)
JsonSendObjSample sjson = new JsonSendObjSample();
RequestEntity<?> req = new RequestEntity<>(sjson, headers, HttpMethod.POST, uri);
 
 
//リクエスト処理(第二引数は受信したボディの変換先の型)
ResponseEntity<JsonRecvObjSample> res = client.exchange(req, JsonRecvObjSample.class);
 
 
//受信オブジェクト
JsonRecvObjSample recvJson = res.getBody();
 
 リクエストオブジェクトにContent-Typeと、POJOを設定し、exchangeを呼び出すだけです。
※clientの作成方法はこの章の先頭に記述。
※注意1:必要ライブラリについて
   クラスパスにjacksonのライブラリがある場合にJson変換の機能が使用可能になることに注意。
  もう少し補足すると、上記のようにJsonを扱えるようにするには、MappingJackson2HttpMessageConverterというクラスを
  clientに追加する必要があるのですが、クラスパスにjacksonのJarがある場合は自動的にSpringがclientに追加するようです。
  (参考:pom.xmlに「jackson-core」「jackson-databind」などを追加すれば動作すると思います) 
※注意2:HTTPヘッダについて
   RequestEntityのHTTPヘッダは、APPLICATION_JSON_UTF8などのJson系のヘッダを指定する必要があります。
  RestTemplate内部のMappingJackson2HttpMessageConverterは、ヘッダが「application/json」のときにボディをJsonへ変換するからです。
  (上記「RestTemplateの仕組み」のリクエスト処理を参照)
  もし、ヘッダの値に無関係にJsonにしたい場合は、自作MessageConverterを作成しclientにadd()するだけです。
 

Stringの内容をリクエスト

//ヘッダ作成
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
 
//リクエスト作成
String strjson = "{\"val\": 123}}";
RequestEntity<?> req = new RequestEntity<>(strjson, headers, HttpMethod.POST, uri);
 
//リクエスト処理(第二引数は受信したボディの変換先の型
ResponseEntity<JsonRecvObjSample> res = client.exchange(req, JsonRecvObjSample.class);  
 
 Stringで指定した"{\"val\": 123}}" がボディ部に設定され、リクエストされます。
 ※clientの作成方法はこの章の先頭に記述。
  

ファイルの内容をリクエスト

//ヘッダ作成
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
 
//ファイルパスを指定
Resource file = new UrlResource(this.getClass().getResource("test_json.txt"));
RequestEntity<?> req = new RequestEntity<>(file, headers, HttpMethod.POST, uri);
ResponseEntity<JsonRecvObjSample> res = client.exchange(req, JsonRecvObjSample.class);
 

【test_json.txtの内容(上記クラスと同じディレクトリに置きます)】

{"val": 123}

 
ボディに指定する型にResourceを指定するだけでファイルの内容を送信できます。
 Resourceは、Springが用意しているファイルリソースの場所を扱うクラスです。
 このクラスがボディとしてリクエストに設定されると、ファイルの中身がボディ部に設定されてリクエストされます。
 Resourceのパッケージ名は、org.springframework.core.io.Resource です。
 
 ※clientの作成方法はこの章の先頭に記述。
 
 

リクエストパラメタをPOST送信する(文字コード指定)

HttpHeaders paramsHeaders = new HttpHeaders();
paramsHeaders.setContentType(new MediaType(MediaType.APPLICATION_FORM_URLENCODED, Charset.forName("shift_jis")));
 
 
//リクエストパラメタ作成
MultiValueMap<String, String> params= new LinkedMultiValueMap<String, String>();
params.add("email", "first.last@example.com");
params.add("name", "太郎");
RequestEntity<?> req = new RequestEntity<>(params, paramsHeaders, HttpMethod.POST, uri);
ResponseEntity<JsonRecvObjSample> res = client.exchange(req, JsonRecvObjSample.class);
 
 
 上記サンプルは、リクエストパラメタを送るとJson(UTF-8)が返ってくるパターンです。
Content-Typeを変更し、ボディに指定する型をMultiValueMapに変えるだけでパラメタ送信できます。
 レスポンスをHTMLとして受け取るパターンは後で示します。
 
 
 ※clientの作成方法はこの章の先頭に記述。
 
 

リクエストパラメタをGET送信する(文字コード指定)

HttpHeaders paramsHeaders = new HttpHeaders();
paramsHeaders.setContentType(new MediaType(MediaType.APPLICATION_FORM_URLENCODED, Charset.forName("shift_jis")));
 
//リクエストパラメタ作成
MultiValueMap<String, String> params= new LinkedMultiValueMap<String, String>();
params.add("email", "first.last@example.com");
params.add("name", "太郎");
 
//URI(GETパラメタ付き)を作成
URI queryURI = UriComponentsBuilder.fromUri(uri)
   .queryParams(params).build().encode("shift_jis").toUri();
 
 
RequestEntity<?> req = new RequestEntity<>(paramsHeaders, HttpMethod.GET, queryURI);
ResponseEntity<JsonRecvObjSample> res = client.exchange(req, JsonRecvObjSample.class);
 
 上記サンプルは、GETパラメタを送るとJson(UTF-8)が返ってくるパターンです。
Content-Typeを変更し、ボディを指定しないだけでGETパラメタ送信できます。
 レスポンスをHTMLとして受け取るパターンは後で示します。
 
 
 ※clientの作成方法はこの章の先頭に記述。
 

 

レスポンスHTML(文字コード指定)を文字列として受信する

//ボディ変換器の文字変換にレスポンスの文字コードを設定する。
CollectionUtils.findValueOfType(client.getMessageConverters(), StringHttpMessageConverter.class)
 .setDefaultCharset(Charset.forName("shift_jis"));
 
//リクエスト作成
RequestEntity<?> req = new RequestEntity<>(HttpMethod.GET, new URI("http://localhost/test.html"));
 
 
//リクエスト処理(レスポンスの型をStringにするだけ)
ResponseEntity<String> htmlRes = client.exchange(req, String.class);
String html = htmlRes.getBody();
 
レスポンス文字コードを変換するには、client内部のボディ変換器StringHttpMessageConverterの設定を変更するか、
 ボディ変換器自体をsetMessageConverters()で置き替えればよいです。
 上記では前者の方法をとっています。
CollectionUtils.findValueOfTypeは、Springのユーティリティで、Listの中から指定の型のオブジェクトを取得します。
 注意点としてはListの中に2個以上同じ型があるとnullを返すことです。
 あと、古いSpringでは、StringHttpMessageConverter.setDefaultCharset()が存在しないので注意です。
レスポンスで返ってきた文字列を文字列として受け取るには、exchangeするときに、String.classを指定するだけです。
 
 ※clientの作成方法はこの章の先頭に記述。
 
 
 

エラーのHTTPステータスを取得する(400、500などのエラーの場合)

try{
   //リクエスト処理
   htmlRes = client.exchange(・・・);
}catch(RestClientResponseException e){
      //ステータスコードの取得
   System.out.println("status: " + e.getRawStatusCode());
}
 
 例外をキャッチして、例外からステータスを取得するだけです。
 
 
 

  その他

 ここでは扱いませんでしたが、次のこともできますので興味を持ちましたら調べていただけたらと思います。
・エラーハンドリング処理
・インターセプト処理(割り込み処理)
・他のボディ変換器(MessageConverter)を使用する(XML変換等)
 
 
 
 
 
 
 

 
 
 Created Date: 2017/09/17
Comments