プロダクトコードなので詳細は非公開ですが、とあるパブリックじゃないWebAPIへのアクセスで激ハマりしたのでその記録などひとつ。
そのWebAPIの仕様書(のようなもの)にはレスポンス内の文字コードについての言及がなく、「メッセージは英語なのかな」って勝手に思い込んでいたのですが、先方の試験用環境にアクセスしたら思いっきりマルチバイト文字だった、というところから始まりました。
完全に文字化けしていてどうしようもない状態だったので、先方に「これ文字コード何ですか?」と確認したら「シフトJISです(ぺこり)」との回答。
ならば、ちょろっと.getBytesして文字コード変換すればいいかな、と思いましたが、妙な違和感。
そもそもhttpclient使っている系のライブラリでこの手の問題(文字化け)って出くわした(解決した)記憶がないのです。
レスポンスの文字コード周りは普通はライブラリがうまく処理してくれるはずですが、まさかと思ってレスポンスの中身を見てみると…
HTTP/1.1 200 OK Date: Sat, 18 Oct 2014 00:00:00 GMT cache-control: no-catch Connection: close Content-Type: text/plain Set-Cookie: xxxxxxx=xxxxxxxxxxxxxxxxxxx; Path=/ (Shift_JISな本文)
charsetがありませんでした。\(^o^)/
あと、最初スルーしてたけどよく見たらno-cacheじゃなくてno-catchだし!
Content-Type: text/plain;charset=Shift_JIS
みたいに返してくれていたらなんの問題もなく、「へー日本語なのね」って程度で済んだでしょうね。
さすがにBody部を見て文字コードを自動判別してくれるほどの親切機能はhttpclientには無いので自力でどうにかする必要が出てきました。
とりわけ文字コード関連はJavaの中で理解しづらい領域で、正しい文字コードが分かってても解決がなかなか大変。パイプの最後にiconvを持ってくれば済むようにはいかないわけです。
結局、一番入口の部分で適切な文字コードを指定してやらないとならないのですが、
- デフォルトだからISO-8859-1っぽいワードを探して、
- そこから変換やってそうな処理を探して、
- 大雑把にブレイクポイント打って、
- ステップステップ…で場所を特定して、
- 文字コード指定できるサブクラスを作って…と思ったらfinalなんで別のクラス作って、
- それを呼び出すFactoryクラスも別なのを作って…
という感じでなんとか問題は解決しましたが、コード読み力が相当落ちてしまっているので存外苦戦してしまいました。
全く邪魔の入らない休日だったから対応できましたが、平日だったら絶対無理ですね...
まとめとしては、WebAPIを提供する場合はヘッダ周りもちゃんと気を使いましょう、というところです。一度公開しちゃうと些細な変更・修正もやりづらいですしね。
余談1
これ、Windows環境だとSJISが普通に食えちゃうので文字化けしないケースだったりします。こういうのに出くわすと開発環境もやっぱりLinuxかMacにしたものか、って考えてしまいますね。
余談2
面倒だからApacheでプロキシしてmod_headers使って強制的にヘッダ書き換えちゃおうか、と試してみましたが、レスポンスのContent-Typeにはmod_headersが効かないという全くの無駄足でした。