webアプリはいろいろ関わってきてるはずなのに、このあたりの話を全然理解してなかったことに愕然とする。
普通のひとには常識なんだろうけど、今回調べたことで体得できたよ。
追記
少し理解が進んだので追記。
TomcatのGETパラメータのデフォルトの挙動
iso-8859-1とみなしてURLエンコードしてくれる。また、GETパラメータには、HttpServletRequest#setCharacterEncodingは適用されない。
useBodyEncodingForURI
これをtrueにすると、GETパラメータであっても、HttpServletRequest#setCharacterEncodingを適用させてくれる。SetCharacterEncodingFilterを使っているのなら、基本的にこれでいいと思うけど、日本語ファイル名とかあるときは、URIEncodingのほうが適当なんだろう。
以下の内容のまとめ
JavaでTomcat6を想定。
以下のようにすればたいがいは問題ないはず。
- システムのページやらコードやらをすべてUTF-8に統一して、POSTで渡すのをUTF-8に統一
- Connectorタグに URIEncoding="UTF-8" を追加してGETメソッドで渡ってくるのををUTF-8に統一
- SetCharacterEncodingFilterを使ってfilterでrequest.setCharacterEncoding("UTF-8")を指定
対応できるもの
- ページ内から送られるPOSTリクエスト全て
- IE6, IE7, FF3, 設定を変えたFF2からのGETリクエスト全て
クライアント側の話
ロケーションバーから入力 | formから入力 | デフォルトのnetwork.standard-url.encode-utf8の値 | |
---|---|---|---|
FF2 | システムのデフォルト文字コードでURLエンコードされるっぽい | ページの文字コードでURLエンコードされる | false |
FF3 | UTF-8でURLエンコードされる | ページの文字コードでURLエンコードされる | true |
IE6,7 | UTF-8でURLエンコードされる | ページの文字コードでURLエンコードされる | - |
- GETならばメッセージヘッダ
- POSTならばメッセージボディ
で渡される。
の文字コードが選択されるということ。
「システムのデフォルト文字コードでURLエンコードするっぽい」と書いてるのはWindowsシステムしか会社にないため。帰ったらMacで試してみよう。
サーバ側の話
JavaでTomcat6を使ったときの話。
GETメソッド(メッセージヘッダ) | POSTメソッド(メッセージボディ) | 手間 | |
---|---|---|---|
setCharacterEncoding | × | ○ | ○ |
URIEncoding | ○ | × | ○ |
new String(request.getParameter("text").getBytes("iso-8859-1"), "UTF-8") | ○ | ○ | × |
request#getParameterしたのをgetBytesで崩して、コンストラクタで再組み立てする
new String(request.getParameter("text").getBytes("iso-8859-1"), "UTF-8")
みたいな。
iso-8859-1を指定するのはURLデコードされた文字列をバイナリとして扱うため?ダメだ。さっそくここの理解が怪しいぜ。
iso-8859-1にしてるのは、メッセージヘッダはデフォルトでiso-8859-1でTomcatがエンコードしてくれるから
この場合は、バイナリがUTF-8でエンコードされてると想定して、UTF-8でデコードしてる。
- メリット
- これをやるだけでGETの場合もPOSTの場合も対応できる
- デメリット
- デフォルト状態のFF2でロケーションバーから入力されたときに対応できない
- request#getPrameterするときに毎回しないといけないので面倒
URIEncoding
server.xmlに
<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="UTF-8"/>
とかって書けばいい。これで必ずUTF-8でURLエンコードされているとみなすようになる。
useBodyEncodingForURI="true"
でもいいけど、下位互換性のためっぽいし、URIEncodingでいいんじゃないかと思う。どっちでもいいと思うけど。
役割が違う。
- メリット
- メッセージヘッダのみなしエンコードを指定できる
- デメリット
- Appサーバ全体で設定しないといけないし、server.xmlに書くのがなんかイヤ
setCharacterEncodingを使う
request.setCharacterEncoding("UTF-8");
みたいな。
メッセージボディのエンコードを指定するだけなので、メッセージヘッダで値を送られてくるGETメソッドには対応できない。
examplesに入ってるfilters.SetCharacterEncodingFilter.javaをパクって使うと便利。
<filter> <filter-name>SetCharacterEncodingFilter</filter-name> <filter-class>jp.foo.filter.SetCharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>SetCharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
- メリット
- メッセージボディのエンコードを一括指定するので、POSTメソッドで送られた場合に一気に対応できて便利