4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

新しい不動産情報ライブラリAPIがSpringで文字化けしたので試行錯誤した話

Posted at

はじめに

4/1にリリースされた国土交通省の「不動産情報ライブラリAPI」とSpringの相性がすこぶる悪く、色々な試行錯誤を経て解決に辿り着いたので記事にしました。Springわかんなくても読める・・・かも?

この記事で伝えたいこと

  • 外部APIが文字化けしたときに問題解決までどうやってたどり着いたか
  • Springで圧縮された形式のAPIレスポンスを受け取る方法

後者だけ知りたい方は「解決策」まで飛ばしてください!

経緯

4月になってエンジニア2年目になった私は、3月に引き続きSpringのシステムの結合テストを行っていました。システムを触っていると、エリア検索等に用いていた市区町村一覧取得機能が突如エラーを吐くようになっていました。エラーにAPIっぽいのが出てたのでそれをクリックして遷移すると・・・

image (1) (1).png

思わずhuh?が飛び出そうになりましたが、リリース前で良かった〜と思うことにしました。重ならなくて本当に良かった・・・。

ということで急にライブラリが一新されてしまい、新APIの使用には申請が必要とのことだったので申請しました。受理されるまでにおよそ1週間かかりました。本当にリリース前で良かった・・・。

APIが文字化けした

申請も通り、早速使ってみようとSpringでAPIを叩くプログラムを作成しました。APIの仕様書に書いてある通り、リクエストヘッダの”Ocp-Apim-Subscription-Key”にAPIキーを設定してリクエストを送るコードを書きました。

public class SearchCityUtil {

	private final String apiKey = "myApiKey";
	
	@Autowired
    private RestTemplate restTemplate;
  
    public List<String> fetchCities(String prefectureCode) {
    // 市区町村一覧取得API
    String url = "https://www.reinfolib.mlit.go.jp/ex-api/external/XIT002?area=" + prefectureCode;
	
    HttpHeaders headers = new HttpHeaders();
    headers.set("Ocp-Apim-Subscription-Key", apiKey);
	
    HttpEntity<String> entity = new HttpEntity<>(headers);
    ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
    String responseBody = response.getBody();
    ...
	}
}

ちゃんと返ってくるかな〜

Untitled (3).png

?????
こうして長い旅が始まりました。

試したこと

色々Springでいじってみたが解決せず

まずは、APIを叩くときの基本的な(といってもよくわかっていない)部分をいじってみました。

  • エンコーディングを色々変えてみる
  • Content-Typeをapplication/jsonに設定してみる
  • Content-Typeの中にエンコーディングを含めてみる

などなど・・・

しかし一向に解決しなかったので、もしかしてAPIがおかしいのでは?という可能性に辿り着きました。

ですが、今のままではSpringとAPIのどちらが悪いのか判断がつきません。
そこで問題の切り分けのためにPostmanという別ツールを試してみることにしました。

PostmanでAPIを叩いてみた

このためだけにインストールするのは腰が重かったのですが、使ってみると便利でとてもいいツールでした。

忘れずにリクエストヘッダにAPIキーをセットして叩いてみると・・・

Untitled (4).png

綺麗な結果が返ってきました!国土交通省さん、疑ってごめんなさい。
でもこれで、Spring側に問題がありそうなことがわかりました。一歩前進です。

しかしここで、「Spring 文字化け API」などとググっても「日本語のみが文字化けする」のようなものしか出てきませんでした。
困った私は、ChatGPTくんの力を借りることにしました。

ChatGPTに聞いてみた

といっても実装の内容について質問してもなかなかいい返事は得られませんでした。
そこで、文字化けそのものを投下してみました。Google検索では絶対にできない使い方です。すると・・・

image (2) (1).png

gzipというキーワードが出てきました。当時の私にとってはなんのことやらサッパリという状態でしたが、今思うと名推理すぎました。やっぱりお前すごいよ。

ただこのときは、散々的外れなことを言われていたためGPTくんのことを全然信用していませんでした。
もう少し色々調べているうちに、あることに気がつきました。

スクリーンショット 2024-04-12 22.27.59.png

よく見たらPostmanのリクエストヘッダにもgzipって書いてあるやんけ!!

この時点で8割くらい確信は得ていましたが、念の為コード上でも確かめてみると、確かにresponseHeaderの中にgzipと書かれていました。さすがに確定か・・・

ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
String responseBody = response.getBody();
// この中にgzipの文字が!
HttpHeader responseHeader = response.getHeaders();

文字化けの正体はgzipだった

原因がほぼ確になったので、あとは「Spring API gzip」などで検索しました。
すると、確かにSpringRestTemplateはデフォルトでgzipを解凍できないとの記述が・・・!
https://tech.excite.co.jp/entry/2021/11/01/133452

代わりにRestTemplateBuilderを用いてRestTemplateを生成したところ、無事に解決しました!!!

今回の問題を通して学んだこと

  • 問題の切り分けは大事

リリースされたばかりのものということもあったので、最初はAPI自体に問題がある可能性を捨てきれていませんでした。ですが、別ツールを使って叩いてみることでその可能性を排除でき、Springの問題として扱うことができました。

今回はPostmanのインストールというちょっと面倒なことをしましたが、まさに急がば回れという言葉の通りでした。

  • 不確実な可能性にすぐに飛び付かずに検証する

途中からgzipっぽいなーという匂いはプンプンしていたのですが、ソースがChatGPTだったこともあり、グッと堪えて証拠集めに走りました。今回は当たっていたから良かったのですが、外れていた可能性もあったので、この姿勢は続けていきたいなと思いました。

専門的になるにつれてGPTくんもそれっぽいことしか言わなくなってくるのは体感としてありますねw

解決策

改めて、不動産ライブラリAPIのレスポンスは、実はgzipという圧縮形式で返ってきます。
他のフレームワークだと自動解凍してくれることも多いそうですが、実はSpringでよく紹介されているRestTemplateデフォルトではgzipに対応していません。

private RestTemplate restTemplate = new RestTemplate();

代わりに、RestTemplateBuilderを用いてRestTemplateを生成してあげると、gzipを解凍できるようになります。

private RestTemplate restTemplate;

// コンストラクタ
public SearchCityUtil(RestTemplateBuilder restTemplateBuilder) {
    // APIが圧縮されているため、restTemplateBuilderを使って解凍する
    this.restTemplate = restTemplateBuilder
        .setConnectTimeout(Duration.ofSeconds(10))
        .setReadTimeout(Duration.ofSeconds(10))
        .build();
}

ちょっとだけ補足

私自身も完全に理解できてはいませんが一応理屈の説明を残しておきます。

まずRestTemplateについて。
このクラスはClientHttpRequestFactoryというインターフェースを用いて、様々なHTTPライブラリにリクエストを送ることを可能にします。

で、このClientHttpRequestFactoryのデフォルトの実装がSimpleClientHttpRequestFactoryというクラスだそうですが、実はこやつがgzipに対応していません。なんてこった・・・

代わりにRestTemplateBuilderを使用してRestTemplateを生成すると、実装がHttpComponentsClientHttpRequestFactoryという、gzip対応しているクラスにしてくれます。やったね。

何のこっちゃという感じかもしれませんが、以下のメソッドで実際のClientHttpRequestFactoryを取得してみるとわかりやすいかもしれません。

 ClientHttpRequestFactory requestFactory = restTemplate.getRequestFactory();

また、RestTemplateBuilderを使用せずとも、手動で明示的にHttpComponentsClientHttpRequestFactoryを指定する方法もあるみたいです。そっちの方がいい気もしてきましたが、今回は割愛します。

終わりに

今回は以下のような理由から記事を書きました。

  • 不動産情報ライブラリAPIという新しいものについての問題だったため
  • 問題解決が綺麗にできたため

ちなみにこの一連の流れで半日(4時間)持っていかれました。4時間で済んだと考えるべきな気はしますが、結構な気力を持っていかれました・・・

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?