このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。
一番大きな問題点は、日本語が通らないことだ。
これには、HTTP, HTML, Servlet, Struts の 4 つの規格が、
それぞれ何かしらの処理で関わっているため、
その原因を正しく理解するのは少し難しい。
そのため、フォームの送信を順番に追いかけ、
どのような流れで日本語が処理されているか考えてみよう。
まず、データを送信するフォームがどうなっているか、
http://localhost:8080/struts-test/filter/edit-info.do
のソースをブラウザで開いて確認する。
以下に、重要な部分だけを引用する。
<form id="filterInfoForm" method="post"
action="/struts-test/filter/update-info.do">
<input type="radio" value="allow"
name="info.defaultAction" />許可
<input type="radio" value="deny" checked="checked"
name="info.defaultAction" />拒否
<input type="text" name="info.note"
size="60" value="" />
<input type="submit" value="更新" />
</form>
符号化方式の指定がないので、フォームのデータは、
「application/x-www-form-urlencoded」
コンテントタイプを使って送信される。
この符号化では、最初に入力項目の名前と値を、
URI のパーセントエンコーディングを使って、
URL で予約されていない文字だけの表現に変形した後、
= 文字 と & 文字を使って、名前=値&名前=値&名前=値
という形に連結し、最終的に ASCII 文字列へと変換する。
なぜ符号化に URL が出てくるのかといえば、
もしこれを GET メソッドで送信する場合、
単に送信先 URL の後ろに ? をつけ、
上記の ASCII 文字列を連結するだけで済むからである。
application/x-www-form-urlencoded は GET 向けなのだ。
ただし、上記フォームでは method 属性が post であるため、
このフォームのデータを送信する際には、
HTTP の POST メソッドが使用される。
POST メソッドの場合は、データを URL に含めず、
別途本体(エンティティボディ)として送信する。
そこでは任意の 8 ビットのバイナリデータが扱えるため、
特に URL で使える表現にこだわる必要はないのだが、
application/x-www-form-urlencoded を使う場合は、
その流儀に従って、ASCII 文字列へと変換する必要がある。
さて、application/x-www-form-urlencoded には、
エンコーディングやコードの問題は生じない。
そもそも使う文字が ASCII の範囲だけに限られるため、
1 文字 = 1 バイトが成立しているからである。
そのため、ASCII の範囲に含まれない文字を、
application/x-www-form-urlencoded で符号化する挙動は、
「未定義となっており、ブラウザに依存している」
この事実は、意外に知られていない。
規格上は、application/x-www-form-urlencoded では、
ASCII 以外のデータの送信を想定しておらず、
そういったデータを扱うためには、multipart/form-data
という異なる符号化を使う必要があるのだ。
とはいうものの、一般的な Web ブラウザでは、
application/x-www-form-urlencoded において、
ASCII 以外の符号化を扱うような拡張がなされており、
その方法については統一されているため、
今回はその拡張に基づいて考えることにする。
となると、フォームの「更新」ボタンが押され、
サーバにデータが送信される前の符号化処理において、
何かしらのトラブルが起きている可能性はないだろうか。
調べてみよう。
上記のフォームにおいて、
既定の処理:「拒否」、備考「内向きフィルタ」として
フォームを送信する場合、どのようなデータになるか考える。
Internet Explorer や Firefox の場合、
以下のような ASCII 文字列に変換されるはずである。
info.defaultAction=deny&info.note=%E5%86%85%E5%90%91%E3%81%8D%E3%83%95%E3%82%A3%E3%83%AB%E3%82%BF
info.defaultAction, deny, info.note は、
元々 ASCII で表現できる範囲の文字だし、
URL で予約されている文字でもないため、
そのままの形で表現されている。
「内向きフィルタ」は、
%E5%86%85%E5%90%91%E3%81%8D%E3%83%95%E3%82%A3%E3%83%AB%E3%82%BF
という長い文字列になっている。
これをよく見てみると、UTF-8 のようだ。
「内」という漢字を UTF-8 で表現すると、
「E5」「86」「85」の 3 バイトの並びで表現されるのだが、
これをパーセントエンコーディングすると、
「%E5%86%85」となるからである。
では、何故 UTF-8 なのだろうか。
HTML では、FORM タグの accept-charset 属性を指定すると、
送信時の文字エンコーディングを強制することができる。
もし、accept-charset 属性が指定されない場合は、
文書自身の文字エンコーディングが代わりとして使われる。
この属性は、本来は multipart/form-data 用なのだが、
ブラウザによってはこの属性の解釈を、
application/x-www-form-urlencoded にも適用する。
今回、JSP で UTF-8 として出力しているので、
生成された HTML も UTF-8 となっている。
そのため、パーセントエンコーディングの際に、
UTF-8 が使われてエスケープされたということだ。
まずは、サーバにデータを送信するまでの間に、
日本語文字列が破損したわけではなさそうである。