とある問題でURL中のクエリ文字列に含まれる「&」を全て「&」に戻したいことがありました。
mod_rewriteを使ってなんとか実現できたんですが、結局もっと正しい修正箇所がわかったので使われませんでした。
が、せっかく新たに得た黒魔術だったので、エントリ化しときます。
URLが例えば本来は「/hoge?foo=1&bar=2&baz=3」となるものの予定が、この他に「/hoge?foo=1&bar=2&baz=3」のようにして渡ってきてしまうものもあるとします。
これを本来想定している「/hoge?foo=1&bar=2&baz=3」にmod_rewriteを使って戻したいわけですが、mod_rewriteでの置き換えでは「s/&/&/g」みたいな全部置換するルールは簡単に書けません。
でこれをやるために、RewriteMapという機能から外部プログラムを呼び出すことで行うことが出来ます。
パラメータはSTDINから渡されるので、そこからプログラムで適当に修正してやれば全置換等もできるわけです。
http://httpd.apache.org/docs/2.4/mod/mod_rewrite.html#RewriteMap
mod_rewriteのための設定追加と、perlなどで書かれたフィルタが必要になります。
/etc/httpd/conf/http.conf
RewriteMap amp_replace prg:/etc/httpd/conf.d/query_amp_replace.pl RewriteCond %{REQUEST_FILENAME} ^/hoge$ RewriteCond %{QUERY_STRING} ^(.*&.*)$ RewriteRule .* /hoge?${amp_replace:%1} [R,L,NE]
- フィルタを宣言するRewriteMapで、スクリプトのパスの前に「prg:」というMapTypeを付ける
- RewriteRuleでフィルタを呼び出す時は「${フィルタ名:パラメータ}」という書式
- QUERY_STRINGに当てたい場合、事前にRewriteCondで%1等にパラメータを取得しておく必要がある
- RewriteCondで「&」がある場合だけフィルタするよう制限しないとループしてしまう
- RewriteRuleで「%」とかをエンコードされてしまわないよう「NE」を付ける
といったところを注意します。
/etc/httpd/conf.d/query_amp_replace.pl
#!/usr/bin/perl $| = 1; while(<STDIN>) { chomp; s/&/&/g; print $_."\n"; }
- バッファリングをさせないようにする(Perlなら「$| = 1」)
- 出力の最後は改行が必要(この例だとchompしなければ"\n"は不要のはず)
という点を注意します。下記ページが参考になります。
Using RewriteMap - Apache HTTP Server Version 2.4
http://httpd.apache.org/docs/2.4/rewrite/rewritemap.html
これでどんな複雑な変換も可能になるわけですが、当然毎回perlなりスクリプトが起動するためパフォーマンスは悪いはずです。
ここでは対象となるプログラムの呼び出しで、かつパラメータに「&amp;」が含まれているときのみ起動するようルールが書いてあるため、そこまでではないと思いますが、注意が必要でしょう。