Ajaxでは、サーバーからデータを受信する際、生のテキスト文字列を使用すると、 responseText,responceXMLそれぞれに文字化けするブラウザがあります。[ 参考:->文字化け調査 ]
これは、Ajaxでの送受信時には、Formの時のようにブラウザが自動でURIエンコード/デコードをしてくれるわけではない、ということに起因すると言えるかもしれません。
一般的には、Formの動作に見られるように、URIエンコードして送受信することで、安全でない文字などが引き起こす、いろいろな問題を回避しようとするわけです。(AjaxでGETやPOSTでの送出時は、ブラウザが自動ではエンコードしてくれない以上、プログラマーの義務ではないかという気がしています。POSTならsetRequestHeader()で、GETなら自前で、、、。)この時、使えるメソッドとして、JavaScriptには、escape()、encodeURI()、そして、encodeURIComponent() があります。
このうち、escape()は、古いメソッドで、ブラウザにより実装が異なるため使わない方が安全です。残りの、encodeURI()とencodeURIComponent()は、ECMAScriptの仕様に従ったメソッドで、現在流通しているブラウザなら共通に動作するはずです。
ありがたいことに、この2つのメソッドは、ページのcharset(Shift_JIS,EUC,UTF-8,,,)が何であっても、UTF-8としてエンコードしてくれます。つまり、「Shift_JISからEUCへ」など異なったcharset環境間でデータを受け渡す場合にも必ず「UTF-8」として明示的にエンコードされたデータとして受け渡せるので安心です。
したがって、もし、サーバーから静的ファイルを取ってくるだけなら、JavaScriptのURIエンコードメソッドで変換した文字列をサーバーにおいておけば、JavaScriptによるAjaxでの受信は、どのブラウザでも同じ種類のデコードメソッドを使えるので文字化けしなくなります。
ところが、サーバーから動的に受信する場合は、あらかじめJavaScriptによるエンコードができません。つまり、サーバー側のPHPなど他の言語でURIエンコードされたものをJavaScriptでデコードしないといけないのです。でも、URIエンコードの実装は、実は、言語によって異なっています。。。そこで、他の言語でのエンコードとJavaScriptエンコード、そしてデコードの違いを実際に確認してみます。今回はPHPです。
もし、問題なくAjaxで受信してデコードされるならそれを使えば良いし、もし、多少の違いがあるなら、そこを修正すれば良いと、いうわけで、、。
比較する文字列
;/?:@&=+$%-_!~*{}[].,()”あa^# '
(#の後ろに空白文字があります)
PHP
$data = ';/?:@&=+$%-_!~*{}[].,()"あa^# '."'";mb_http_output ( 'UTF-8' );$data = mb_convert_encoding($data,"UTF-8",mb_internal_encoding());$html = "urlencode() : ".urlencode($data);$html .= "rawurlencode() : ".rawurlencode($data);echo($html);
urlencode() :
%3B%2F%3F%3A%40%26%3D%2B%24%25-_%21%7E%2A%7B%7D%5B%5D.%2C%28%29%22%E3%81%82a%5E%23+%27
rawurlencode() :
%3B%2F%3F%3A%40%26%3D%2B%24%25-_%21%7E%2A%7B%7D%5B%5D.%2C%28%29%22%E3%81%82a%5E%23%20%27
–>PHPがエンコードしない文字列
-_.a
注意:urlencode()は、空白文字を「+」に変換し、rawurlencode()は「%20」に変換するという違いがあります。
JavaScript
var data = ';/?:@&=+$%-_!~*{}[].,()"あa^# '+"'";var enchtml = 'encodeURI() : '+encodeURI(data) enchtml + 'encodeURIComponent() : '+encodeURIComponent(data)document.write(enchtml)
encodeURI() :
;/?:@&=+$%25-_!~*%7B%7D%5B%5D.,()%22%E3%81%82a%5E#%20'
–>encodeURI()がエンコードしない文字列
;/?:@&=+$-_!~*.,()a#'
encodeURIComponent() :
%3B%2F%3F%3A%40%26%3D%2B%24%25-_!~*%7B%7D%5B%5D.%2C()%22%E3%81%82a%5E%23%20'
–>encodeURIComponent()がエンコードしない文字列
-_!~*.()a'
結論1
つまり、PHPとJavaScript(ECMAScript)のURIエンコードは、違うのです。
では、デコードするとどうなるでしょう?
デコード : PHP urlencode() → JS decodeURIComponent()
PHP urlencode()でエンコード
%3B%2F%3F%3A%40%26%3D%2B%24%25-_%21%7E%2A%7B%7D%5B%5D.%2C%28%29%22%E3%81%82a%5E%23+%27
JS decodeURIComponent()でデコード
;/?:@&=+$%-_!~*{}[].,()”あa^#+'
–>encodeURIComponent()はエンコードしないが、decodeURIComponent()がデコードした文字列
!~*()'
(-_.aは、PHP がエンコードしません。)
おお、encodeURIComponent()でエンコード出来なかった文字までデコード出来てる!
てことは、PHP–>JS なら、「utf-8化してurlencode()」–>「decodeURIComponent()」で、使えるのでしょうか?
うーん、、、でも、これって、ECMAの仕様的にOKなんでしょうか?よくわかりません、、、。
この組み合わせの場合、最大の問題は、urlencode()によって「+」に変った空白文字がそのままなため普通の「+」と区別がつかなくなること。 ということで、次は、空白文字をencodeURIComponentと同じ「%20」に変換する、rawurlencode()で試してみましょう。
デコード : PHP rawurlencode() → JS decodeURIComponent()
PHP rawurlencode()でエンコード
%3B%2F%3F%3A%40%26%3D%2B%24%25-_%21%7E%2A%7B%7D%5B%5D.%2C%28%29%22%E3%81%82a%5E%23%20%27
JS decodeURIComponent()でデコード
;/?:@&=+$%-_!~*{}[].,()”あa^# '
–>encodeURIComponent()はエンコードしないが、decodeURIComponent()がデコードした文字列
!~*()'
(-_.aは、PHP がエンコードしません。)
おお、こんどこそデコード出来てる!
、、、他に問題ないかなぁ、、、(^^;。
結論2
PHPは、 utf-8化 + rawurlencode() → JS decodeURIComponent() この組み合わせで、行けそうかも