Servletの文字化け

今更ながら、javax.servlet.ServletRequestクラスの
getParameterメソッドで日本語を取得したときの文字化けについてメモ。
専門学校生を教えてる中でいろいろ質問が出たので。


通常は、何もやらずにいきなり日本語を取得すると
文字化けしちゃいます。理由は以下の通り。


1) ブラウザのFORMで「あいう」と入力して送信
2) 送信時の「あいう」の文字コードは0x82A0、0x82A2、0x82A4(Shift_JISの場合)
3) Servletに渡る際は、上位に00がついてしまう(0x0082、0x00A0、0x0082、0x00A2・・・)


そこで、値を取得した後に以下のコードを記述すると・・・

String str = request.getParameter("param");
byte[] bytes = str.getBytes("iso-8859-1");
str = new String(bytes, "Windows-31J");

4) str.getBytes("iso-8859-1")によりバイト配列に変換(0x82、0xA0、0x82、0xA2・・・)
5) new String(bytes, "Windows-31J")により、バイト配列を元に正しいUnicodeの「あいう」を生成


5)で指定したWindows-31Jは、元のデータがShift_JISであったことを指定してます。
Windows-31Jに関してはこちら
これで正しく日本語を取得できます。


ここまではJ2EE1.2(Sevlet2.2)の話。
J2EE1.3(Servlet2.3)からは、javax.servlet.ServletRequestクラスの
setCharacterEncodingメソッドで、上記の処理をしてくれます。
使用例はこんな感じ。

request.setCharacterEncoding("Windows-31J");
String str = request.getParameter("param");

現在市販されてるAPサーバはほとんどがJ2EE1.3以上なので、
古い環境を利用するんじゃなければこっちの方がいいかと。


ただし、setCharacterEncodingメソッドは
リクエストのボディに対して適用されるとありますが、
リクエストヘッダに対しては明確な仕様が存在してないそうです。
そこで、Tomcatの場合は4.1.29/5.0.16からsetCharacterEncodingメソッドの
指定はヘッダに対しては適用されなくなりました。
ヘッダに対して(つまりGETで日本語を渡したとき)も適用したい場合は、
server.xmlのConnectorタグのuseBodyEncodingForURI属性を
trueにする必要があるようです。
ちなみにTomcat4.1.x系はデフォルトがtrue、5.0.x系はfalse。
(でももしかしたら一部デフォルトが違うのがあるかも?)
う〜ん、ほんとにややこしい。。。
とりあえずTomcatを利用する際はserver.xmlの記述を確認しろと。


他のAPサーバの状況については、id:hyperash:20040309さんが説明されてました。
うちはJRunを利用してるのでそのうち調べとかなきゃ。


んでも一番重要なのは、クライアントが何の文字コード
日本語を送ってきてるのか・・・だよなぁ。
WindowsならShift_JISLinuxならEUC-JP・・・かと思いきや、
LinuxからShift_JISで送信することもできてたし。
っていうか、User-Agent自体いくらでも偽装できるから
そっから正しい文字コードを取得するって考え自体無理。


JavaHouseでは、文字コードの判別ができる適当な文字を
hiddenの入力項目に必ず用意するって案を出してる人がいたなぁ。
これは確かに有効かも。ちょいかっこわるいけど。
ログイン時だけこれで判定して、以降は判定結果を
格納したセッションかCookieを利用すればいいのかな。


うちは特定ユーザを対象にした業務アプリがメインだから
制限にしちゃえばいいんだけど、インターネット環境の
アプリを開発してる人たちはどうやってるんだろうなぁ。。。
新人の頃からこの辺の問題は何も解決してない気がする。


何か間違ったことを書いていたらご指摘くださいm(_ _)m