このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。
どうも getParameter 系の挙動があやしそうだ。
Struts はフォームからのデータの読み取りに、
getParameter 系のメソッドを利用しているため、
getInputStream や getReader が呼び出されることはない。
そこで HttpServletRequest#getParameter を調べてみよう。
UpdateFilterInfoAction#execute 内に以下のように書く。
String note = request.getParameter("info.note");
if (note != null) {
System.err.println("Encoding: "
+ request.getCharacterEncoding());
System.err.println("Note: " + note);
System.err.println("Length: " + note.length());
}
コンソール又は Tomcat のエラーログを見てみると、
見事に文字化けしていることが分かる。
Encoding: null
Note: ??????????????£??≪???
Length: 21
この結果から分かることは以下のとおりだ。
クライアントが文字エンコーディングを送信していないので、
Servlet は既定の ISO-8859-1 でパラメータを解釈した。
送信した文字列は「内向きフィルタ」7 文字であるが、
これを UTF-8 としてバイト配列にすると 21 バイトになり、それを ISO-8859-1 として文字に戻すと 21 文字となる。
UTF-8 のバイト表現を ISO-8859-1 として解釈したため、
バイトと文字の対応付けがおかしくなり文字が化けた。
さて、一見クライアントの責任のようにも読み取れるが、
この場合、クライアントには責任はない。
getCharacterEncoding() で有効な値を読み出せるのは、
クライアントが Content-Type に追加パラメータとして、
charset パラメータを送信したときである。
これは、例えば以下のような Content-Type が相当する。
Content-Type: text/html; charset=Shift_JIS
charset パラメータは、文字に関する情報なので、
通常メディアタイプが text/* の場合にしか使われない。
フォームは application/x-www-form-urlencoded なので、
「application」タイプである。
この場合、パラメータの指定は強制されない。
寧ろ、application/x-www-form-urlencoded を定めている、
HTML の規格上でパラメータについて言及されていない以上、
クライアントが charset パラメータを送信するわけがない。
では、文字化けを防ぐにはどうすれば良いか。
簡単に言えば、文字エンコーディングを変えれば良いのだ。
幸い、HttpServletRequest には、
setCharacterEncoding という setter が用意されており、
文字エンコーディングを変更することができる。
このメソッドを呼び出して UTF-8 を指定すれば、
getParameter でデータを読み出した際に、
文字化けを防ぐことができるのである。
ただし、setCharacterEncoding を使う場合、
getParameter や getReader 等のメソッドを呼ぶ前に、
setCharacterEncoding を呼ばなければならない制約がある。
それなら、関連付けられている Action の、
execute メソッドの最初で呼べばと考えそうだが、
残念ながらこれでは上手くいかない。
なぜならば、Action が呼ばれる前に、
Struts によって ActionForm が初期化されるからだ。
Struts が ActionForm に値を設定する際には、
HttpServletRequest の getParameter 系メソッドを呼ぶ。
そのため、Action で setCharacterEncoding を呼んでも、
時すでに遅し。ActionForm には化けた値が入っているのだ。