mod_rewrite と Output Filter で URL を美しく

URL の重要性は増す一方だ。Web 2.0 と呼ばれる世界、あるいは SemanticWeb においても、URL (URI) それ自身が表す意味というのは重要になってきた。そんな話を前回の本冊子に書いたのだけれども、今回はその実践編である。

Wiki の URL を綺麗に

Wiki は相変わらず広く使われている。実際、便利だ。しかし多くの Wiki はURL が美しくない、と思う。これをなんとかしてしまおう。Wiki のコードに手を入れずに済ませるためにまず思いつくのは、mod_rewrite による書きかえだ。mod_rewrite では「ブラウザから置くられてきた URL」を「パス名やURL」に対応付けることができる。

FreeStyle Wiki の場合

FreeStyle Wiki という perl で書かれている Wiki がある。perl で書かれていて、かつ国産のものの中では人気の高いものだ。私自身もあちこちで大変お世話になっている。この Wiki で使われる URL は以下のようなパターンになっている。

 http://example.jp/wiki.cgi
 http://example.jp/wiki.cgi?page=FrontPage
     トップページを表示
 http://example.jp/wiki.cgi?page=HogeMoge
     ページ「HogeMoge」を表示
 http://example.jp/wiki.cgi?action=NEW
     新規にページを作成
 http://example.jp/wiki.cgi?action=EDIT&page=HogeMoge
     ページ「HogeMoge」を編集
 http://example.jp/wiki.cgi?action=LIST
     ページの一覧を表示
 http://example.jp/wiki.cgi?action=SOURCE&page=HogeMoge
     ページ「HogeMoge」のソースコードを表示
 http://example.jp/wiki.cgi?action=SEARCH
     検索フォームを表示
 http://example.jp/wiki.cgi?word=Hoe+Moe&t=and&action=SEARCH
     単語「Hoe」かつ「Moe」のあるページを検索
 http://example.jp/wiki.cgi?word=Hoe+Moe&t=or&action=SEARCH
     単語「Hoe」または「Moe」のあるページを検索
 http://example.jp/wiki.cgi?action=PDF&page=HogeMoge
     ページ「HogeMoge」のPDFを生成
 http://example.jp/wiki.cgi?action=RSS
     この Wiki の RSSを生成
 http://example.jp/wiki.cgi?action=LOGIN
     この Wiki にログイン

これを以下のように書きかえてみよう。原則として、Query String はコマンド名のみ。ページ名そのものを Query String ではない URL に埋めこむ、という方針である。検索機能については /search、RSS については /rss という URL を予約し、ソースの表示や PDF については「拡張子」のように見せることにする。

 http://example.jp/
     トップページを表示
 http://example.jp/HogeMoge
     ページ「HogeMoge」を表示
 http://example.jp/?new
     新規にページを作成
 http://example.jp/HogeMoge?edit
     ページ「HogeMoge」を編集
 http://example.jp/?list
     ページの一覧を表示
 http://example.jp/HogeMoge.wiki
     ページ「HogeMoge」のソースコードを表示
 http://example.jp/search
     検索フォームを表示
 http://example.jp/search?word=Hoe+Moe&t=and
     単語「Hoe」かつ「Moe」のあるページを検索
 http://example.jp/search?word=Hoe+Moe&t=or
     単語「Hoe」または「Moe」のあるページを検索
 http://example.jp/HogeMoge.pdf
     ページ「HogeMoge」のPDFを生成
 http://example.jp/rss
     この Wiki の RSSを生成
 http://example.jp/?login
     この Wiki にログイン

インストール

開発元よりコードを入手し、適当な場所に置く。原稿執筆時点 (2005/8/7) で最新版は 3.5.8 だ。これを /usr/local/wiki にインストールすることにする。

なお、Debian (sarge) をターゲットマシンの OS であると仮定し、Apache はApache2 を使うことを前提て説明する。また、ドメイン名は example.jp であるとし、このサーバ全体を Wiki で構築するものとする。つまり / 以下をFreeStyle Wiki で構築するとする。

まず、適当なディレクトリに wiki3_5_8.zip をダウンロードした後から始める。

% ls
wiki3_5_8.zip
% unzip wiki3_5_8.zip
% mv wiki_3_5_8 /usr/local/wiki
% cd /usr/local/wiki
% mkdir backup attach pdf log
% chown -R www-data:www-data data backup attach pdf log config
% find . -type f -print | xargs chmod 644
% find . -type d -print | xargs chmod 755
% chmod 755 wiki.cgi

次に Apache の設定ファイルを編集する。

ファイル /etc/apache2/sites-available/example.jp に

<VirtualHost *>
 ServerName example.jp
 DocumentRoot /usr/local/wiki
 ScriptAlias /wiki.cgi /usr/local/wiki/wiki.cgi
 <FilesMatch "\.(pm|dat|wiki|log)$">
   Deny from all
 </FilesMatch>

</VirtualHost>

と書き、

# a2ensite tkusano.jp
# /etc/init.d/apache2 reload

を実行。http://example.jp/wiki.cgi にアクセスしてみよう。正しく設定できていれば標準の Wiki の FrontPage が表示されるだろう。おかしな場合はエラーログを見て適切に修正しよう。

ここまでは普通のインストール手順であり、URL は「美しくない」ままだ。

ページの URL の Rewrite

さて、mod_rewrite を使って書きかえしていこう。まず単純にページ「HogeMoge」を表示、といったルールの適用から。

あらかじめ mod_rewrite モジュールの組み込み自体は

# a2enmod rewrite

して実行しておく。

次に前述のApache の設定、ScriptAlias の後に以下のような行を追加する。

RewriteEngine On
RewriteRule ^/$ /wiki.cgi?page=FrontPage [L,PT]
RewriteRule ^/wiki\.cgi$ /wiki.cgi [L,PT,QSA]
RewriteRule ^/theme/(.*)$ /theme/$1 [L,PT]
RewriteRule ^/(.*)$ /wiki.cgi?page=$1 [L,PT]

1行目は mod_rewrite を有効にする宣言である。

2行目は http://example.jp/http://example.jp/wiki.cgi?page=FrontPage と同等にする書きかえである。末尾の「L」により、rewrite ルール群の適用の終了を宣言し、「PT」により、mod_rewrite よりも後段の ScriptAlias による処理に投げる。

3行目は CGI 呼び出しタイプの URL である

http://example.jp/wiki.cgi?page=HogeMoge?action=ACTION

的な URL を活かす設定である。これが無いと、FreeStyle Wiki が生成したHTML に埋めこまれている URL のほとんどが無効になってしまう。これは本来の目的からすると邪道であるが、とりあえず入れておく。末尾の「QSA」は、ブラウザから送られてきた CGI のパラメータを CGI に「渡す」ために必要だ。

4行目は生成される CSS の URIを実ファイルにマップするためのルールだ。これは wiki の設定ファイルである setup.dat 中の theme_uri の内容によって変わる。

最後は

http://example.jp/HogeMoge

のような URL を

http://example.jp/wiki.cgi?page=HogeMoge

に「書きかえる」ルールである。

アクションの URL の Rewrite

さて、各種アクションを書き変えていく。CGIのパラメータは RewriteRule のみではパターンマッチできないので、RewriteCond で QUERY_STRING をテストすることにより実現していく。たとえば、「編集」アクションの書きかえは以下のようになる。

RewriteCond %{QUERY_STRING} ^edit$
RewriteRule ^/(.*)$ /wiki.cgi?page=$1&action=EDIT [L,PT]

また、ソースの表示やPDFの生成は「拡張子」によるマッピングを行う。また、検索については /search、RSS については /rss という予約 URL を使うことにしたので、それぞれ RewriteRule によって定義する。

RewriteRule ^/(.*)\.wiki /wiki.cgi?page=$1&action=SOURCE [L,PT]
RewriteRule ^/(.*)\.pdf /wiki.cgi?page=$1&action=PDF [L,PT]
RewriteRule ^/search$ /wiki.cgi?action=SEARCH [L,PT,QSA]
RewriteRule ^/rss$ /wiki.cgi?action=RSS [L,PT]

実際にはこれ以上に必要となるルールがあるだろうが、リクエストされたURL の書き変えについては、ひとまずここまで。

出力の書きかえ

ここまでの説明ではブラウザが送ってきた URL を、いかに CGI やファイルにマップしていくか、という話しであったが、こんどは逆に、CGI の出力をブラウザに送り返す前に書きかえる話である。

HTML 中には a 要素によって、リンク先の URL が指定されているが、Wiki のプログラムは mod_rewrite による書き替えなど関知しているわけではない。あくまでも元の仕様通りの URL を組み立てて出力するのみである。

この HTML の書き替えは mod_rewrite ではできない。Wikiのプログラムのほうに手を入れるのが、ある意味正当ではあるのだろうけど、それを避けたい場合もある。CGIのソースコードに手を入れることができたとしても、URLの生成に関する処理がコードのあちこちに分散していたりすると、かなりやっかいである。

CGIによっては出力するURLの見た目を変更できることもあるが、FSWikiの場合には難しい。

このような場合には、Apache 2 から公式に実装された「フィルタ」の機能を使うことができる。

フィルタ

Apache 2 の新機能で特徴的なものに「フィルタ」がある。これは、ブラウザからの入力や、Apache 本体や CGI などからの出力を加工するための汎用的なフレームワークである。

SSL や SSI は、この「フィルタ」の機能を使って実装されているのであるが、ユーザがあらたに自由にフィルタを作成することもできる。

性能の面から言えば C でフィルタを実装したモジュールを書く、あるいはmod_perl2 のフィルタ API 対応の機能を使うなどするのが良いのであるが、ここではお手軽な方法として、mod_ext_filter モジュールを使って外部のプログラムをフィルタとして援用することにする。

mod_ext_filter は Apache からの出力を外部のプログラムに標準入力として渡し、そのプログラムの標準出力からの出力をあらためて Apache の出力にしてしまう、というものだ。

これを使えば、コマンドライン用のフィルタアプリケーションを作る間隔でApache のフィルタ機能を使った拡張を実装できてしまう。

たとえば、実装コマンドを /usr/local/wiki/rewrite.pl とし、かつ、text/html の「出力」のみを処理するフィルタ urlrewrite の定義と適用は次のようになる。

ExtFilterDefine urlrewrite cmd=/usr/local/wiki/rewrite.pl intype=text/html mode=output
SetOutputFilter urlrewrite

フィルタはファイルの読みこみや、CGI の出力を Apache が受けとった後の処理が渡されるので、この定義により wiki.cgi の出力が rewrite.pl に渡され、それがブラウザに送り返されることになる。

フィルタプログラム

詳細の解説は省略する。perl と HTML::Parser モジュールを作った、非常に単純なプログラムだ。CGI の出力する HTML を解析し、a タグの href 属性にぶちあたって、かつ、その URL が wiki.cgi で始まるものであれば、適当に書きかえている、というものだ。form タグの action 属性、link タグなどの href 属性については考慮していない。

効率を上げるならキャッシュのことを考えなくてはいけないし、また、mod_perl2 を使うように書きかえたほうがいい。また、セキュリティの問題がないかチェックしなくてはならない。

最後に

入力のURLと、出力のURLの書き替えを別々に定義している、というのもまたやっかいだ。どちらかを書き替えたら、どちらかも同期しなくてならない。このあたり、より楽にならないだろうか。

2006.8.21 注記

この記事にある方法だけでは実際に FSWiki を利用するには不都合があったりするが、それは各自何とかしましょう。

2007.2.8 追記

一部、文章を修正。