「コア・サーブレット」(原書)は昨年(2003)8月に第二版が出まして、大幅に改定&増補されました。私(岩谷)自身の経験も含めて、ここに、有意義なメモを作っていこうと思います。 (1)FORMデータ, GET/POST, multipartの問題 第一版16章には、FORMデータをGETでなくPOSTで送るためにはENCTYPE="multipart/form-data"を指定すればよい、と書かれていますが(訳書p371)、これは間違いです。HTML4を正しく実装したブラウザなら、METHOD="POST"を指定しなければmultipart/form-dataでもデータはGETで送られます。ただし、METHOD="POST"を指定しただけでENCTYPE="multipart/form-data"を指定しなければ、送られるデータの形式はapplication/x-www-form-urlencodedとなり、すべてのFORM入力データがURLエンコードされたワンデータとしてリクエストボディになります(URLにくっつかない)。 ですから、POSTでかつ、複数のFORM入力データをマルチパート(複数データ)として送るためには、METHOD="POST"とENCTYPE="multipart/form-data"の両方を指定しなければなりません。そしてこのときにかぎり、UnicodeでBasic Latin(ISO-8859-1)の文字(UTF-8で1バイトになる文字)にかぎりURLデコードが行われますが(というか、もともとURLエンコードされないデータが送られる)、UTF-8の16ビット文字(24ビットで表現される文字)は力のようなエンコード化文字列(HTML規格のいわゆる文字実体, character entity)で表されます。そしてしかしこれらを、ブラウザは正しい文字で表示します。 しかし一方、Javaサーブレットの規格は、POSTでもデータ形式がx-www-form-urlencodedでなければrequest.getParameter()しない/できない、と(なぜか)言い張っていますから、サーブレットで使うためのFORMではMETHOD="POST"とENCTYPE="multipart/form-data"の両方を指定するとパラメータがnullになってしまいます。ResinもTomcatもこの規格には忠実です。 まとめとして繰り返しますと、ENCTYPE="multipart/form-data"だけならデータはGETで送られます。データ形式は当然application/x-www-form-urlencodedです。また、METHOD="POST"だけを指定すると、データ形式はワンデータのapplication/x-www-form-urlencodedとなります。いずれもURLデコードされていない、ナマのリクエストデータです。URLデコードされた(ないし元々URLエンコードされていない)マルチパートデータ(ただしUTF-8,16bit文字は力のようなエンコード化文字列)が欲しければmultipart/form-dataとPOSTの両方を指定しますが、これはサーブレット用には使えません。サーブレットはあくまでも、元の文字コードや文字実体そのものではなく、URLエンコードされたデータ(すなわち7bit ASCII文字列)をFORMデータ等として要請するのです。ここが、どうやら、カンジンな点のようです。 そこで、いずれにしてもサーブレット側では、力権...のようなエンコード化文字列,“文字実体”ならばそれを、JavaのStringすなわち, 21147 27177 ...という16ビット値の列に変換しなければ、内部的なテキスト処理等はできません。しかしFORM HTML側でcharsetを明示的/現物的(=本文文字として)に指定し、サーブレット側でもリクエストのエンコードをやはり明示的に指定すれば、HTML4の規格のおかげで、文字実体ではなくUTF-8のコード値そのものが…もちろんURLencodeされて…送られますし、個々のパラメータへの切り分けと8ビット部分のURLデコードはgetParameter()側でやってくれますから、たいへん気が楽です(なんといってもJavaのStringがUnicodeだからですね!)。 単純な結論としては、FORMのあるHTMLの上に日本語の本文があるなら、その文字のエンコード(文字コード種)でrequest.setCharacterEncoding()をgetParameter()の前に呼んでおく。現物的に日本語本文がなく、しかしFORMデータがISO-8859-1(ASCII)とはかぎらないときは、request.setCharacterEncoding("UTF-8")を呼んでおく、でよいと思います。そうするとgetParameter()は単なるURLデコードにとどまらず、完全なJavaのString(Unicode/UTF-8列)を作ります。ただしブラウザによっては、このスタンダードから逸れているものの可能性もあります(現実に私は知りませんが)。いずれにしても、私の苦い経験から言えば、FORMはあくまでも(可搬性を尊ぶ)Webアプリ側のリソースですから、アプリを作る側に主体性があります。 (互いに異なるMETA ... charset= ...の指定と現物日本語本文の両方が、FORM HTML上にあるときは、送信でもcharsetが優先され、日本語本文のほうは文字化けします。) なお(上の説明文は非常にややこしいので)、ご参考までに、日本語本文のない、英語だけのHTML上で、「ローカル権力」という計6文字の日本語をFORM入力したときの、サーブレット側の結果を、下表に示します(Mozilla 1.6/Linux/Resin 2.1.12)。これらから、次のことが自覚できます: (1)request.getParameter()にパラメータのエンコード情報(文字コード情報)を教えてやることは必須である。 (2)しかしFROM HTML側に正しいcharset情報がないと、文字ではなくいわゆるHTMLの文字実体(character entity)が送られてしまうことがある。それらはHTML上では正しい文字で表示されるが、サーブレット内部でテキスト処理等に使うためには、JavaのStringに変換しなければならない。たとえば、こんなコードを使って。 | ||||||||||||||||||||||||||||||||||||||||||||||||
【日本語本文のない(英語のみの)FORM HTMLの場合】 ●サーブレット側にrequest.setCharacterEncoding("UTF-8");があると…
●サーブレット側にrequest.setCharacterEncoding()呼び出しがないと…
[別記A--文字実体(character entity)]
[別記B--UTF-8の24ビット文字コード列(getParameter()にエンコード情報を与えた場合)]
[別記C--UTF-8の24ビット文字コード列(getParameter()にエンコード情報を与えなかった場合)]
(「¨」のところは、実際は0xa8という1バイト値。これはたまたま、Microsoft Extended-ASCIIでウムラウト文字にあたる。) | ||||||||||||||||||||||||||||||||||||||||||||||||
URL encodeとUTF-8に関する基本資料 --この項終わり-- |