spring
spring-boot

RestTemplateでレスポンスのContent-Typeを変更する。

この記事で紹介すること

この記事ではSpringのRestTemplateで受け取ったレスポンスのContent-Typeを変更し、任意のHttpMessageConverterを実行させる方法を考えたのでメモとして記載します。

外部のAPIサーバを利用した際に、JSONフォーマットのbodyにContent-Type: text/plainで帰ってくることがありました。レスポンスのJSONに対応したBeanに値をバインドさせようとしてもContent-Typeがtext/plainのため、期待したHttpMessageConverterにマッピングされません。
そこで、RestTemplateにレスポンスのヘッダを変更するClientHttpRequestInterceptorを登録し解決しました。

ClientHttpRequestInterceptorの実装とRestTemplateBuilderの利用方法は以下のサイトを参考にしました。

またBing Speech API を対象に今回の内容を実装したものを以下のリポジトリに置いています。認証などの要素も入っているため、今回の説明とは多少異なります。
https://github.com/masato-ka/speech-api-proxy-service

ClientHttpRequestInterceptorの実装

ResponseHeaderInterceptor.java
@Component
public class ResponseHeaderInterceptor implements ClientHttpRequestInterceptor {

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
            throws IOException {        
        ClientHttpResponse response = execution.execute(request, body);//(1)
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);//(2)
        return response;
    }

}

(1)実際のリクエスト実行処理
(2)リクエスト実行後、レスポンスオブジェクトのContentTypeをapplication/jsonで上書きします。

RestTemaplateの設定と実行

RestClient.java
@Component
public class RestClient {

        private final String apiUri = "https://hoge.com/api/v1/foo"
        private final RestTemplate restTemplate;

    public RestClient(RestTemplateBuilder restTemplateBuilder,
              ResponseHeaderInterceptor interceptor){//(1)
        restTemplate = restTemplateBuilder.additionalInterceptors(interceptor).build();//(2)
    }

    public Result request(){

        URI targetUri = UriComponentsBuilder
                                      .fromUriString(apiUri)
                                      .build()
                                      .toUri();

        Result result = restTemplate.getForObject(targetUri, Result.class);//(3)
        return result;
    }

}

(1) ResponseHeaderInterceptorをInjection
(2) RestTemplateBuilderを通してResponseHeaderInterceptorをRestTemaplateに設定
(3) リクエストを実行、この際にResponseHeaderInterceptorの処理がフックされる

まとめ

そもそもサービス側がContent-Typeを正しく返してくれればいいのですが、コントロールできない他者のサービスだったため、このような方法をとりました。
 そもそもInterceptorを実装しない簡単な方法があるかもしれません。