Mojoliciousで自由に「URL」を生成するテクニック

URLを作成するための基本

url_forを使うと、URLを指定することができます。MojoliciousはCGIや組み込みWebサーバーなどの複数の環境で実行することができますがurl_forメソッドでURLを記述すると、どの環境でも実行できるURLを記述することができます。

url_for('/user/register');

またルート名を渡すと何ができるかというと、URLに含まれるパラメータを復元できます。す。

get '/foo/bar/:id'; 

url_for('foobarid', id => 3); 

Mojoliciousでクエリ文字列を含むURLを生成する

Mojoliciousでクエリ文字列を含むURLを生成するにはqueryメソッドを使用します。

<%= url_for('/path')->query(foo => 'a', bar => 'b') %>;

リクエストURLを復元する

リクエストURLを復元するにはurl_forメソッドの引数に何も指定しないか、currentを指定します。

% my $url = url_for;
% my $url = url_for('current');

「:id」などの記述でキャプチャされたURLも正しく復元されます。

/entry/:id

ただしクエリ文字列の部分は自動的に復元されないので、復元したい場合はurl_withを使う必要があります。

% my $url = url_with;
% $url->query([name => 'Ken', age => '19']);

このようにすると、検索条件に対して、さらに絞り込んだ検索を簡単に記述することができます。

またqueryメソッドが値を設定する場合は、自身のMojo::URLオブジェクトを返却することを覚えておけば、次のように一行で記述することもできます。

%= url_with->query([name => 'Ken', age => '19']);

クエリ文字列の取り扱い

クエリ文字列を解析したい場合はMojo::Parametersのparseメソッドを使用します。

$params->parse('foo=b%3Bar&baz=23');

一般的な利用方法としてはurl_forメソッドでMojo::URLオブジェクトを生成した後に、Mojo::URLのqueryメソッドでMojo::Parametersオブジェクトを取得してからparseメソッドを使うことが多いでしょう。

次のようにするとURLにクエリ文字列を設定することができます。

# テンプレートの中
% my $url = url_for('/search');
% $url->query->parse('title=perl&name=ken');

クエリ文字列を置き換える

クエリ文字列を置き換えるには次のようにリスト形式で渡します。

$url->query(name => 'taro', price => 1900);

上記の例の場合は以下のように置き換わります。

# 前
title=perl&name=ken

# 後
name=taro&price=1900

クエリ文字列のマージ

クエリ文字列をマージするには配列のリファレンスとして渡します。

$url->query([name => 'taro', price => 1900]);

次のようにマージされます。値が同じものは置き換えられます。

# 前
title=perl&name=ken

# 後
title=perl&name=taro&price=1900

クエリ文字列の追加

クエリ文字列を追加するにはハッシュのリファレンスとして渡します。

$url->query({name => 'taro', price => 1900});

次のように追加されます。

# 前
title=perl&name=ken

# 後
title=perl&name=ken&name=taro&price=1900

リクエストのクエリ文字列を受け継いだURLを生成する - url_with

たとえば、検索条件は変更せずにページ番号だけを変更したいと思うときがよくあると思います。クエリ文字列の部分はそのまま使いたいのだけれど、一部の条件は変更したいという場合です。

このような場合は>url_withヘルパーを使います。クエリ文字列を受け継ぐほかはurl_forと使い方は同じです。

%= url_with '/foo'

次のように記述すると、ページ番号だけ変更できます。

%= url_with->query([page => 2])

リバースプロキシを利用したときにホスト名、ポート番号、パスが正しく認識されるようにする

ApacheなどのWebサーバーをリバースプロキシにして、Mojoliciousアプリケーションにディスパッチするときに、そのままの設定では絶対URLを出力するredirecto_toで正しくURLが出力されません。たとえば以下のような設定を行ったとします。

  <VirtualHost *:80>
    ServerName perlcodesample.com
    <Proxy *>
      Order deny,allow
      Allow from all
    </Proxy>
    ProxyRequests Off
    ProxyPreserveHost On

    ProxyPass /gitweblite http://localhost:10010/gitweblite keepalive=On
    ProxyPassReverse /gitweblite http://localhost:10010/gitweblite

    RequestHeader set X-Forwarded-Proto "https"
  </VirtualHost>

注意点として、最近のLinuxはselinuxが有効になっているのですが、これが有効になっていると「Service Temporarily Unavailable」となって、Apacheからアプリケーションに接続ができません。設定を変えるかselinuxを無効にします。

# /etc/selinux/config
SELINUX=disabled

正しく認識させる方法

以下のような記述を追加しましょう。

# アプリケーションクラス
sub starup {
  my $self = shift;
  
  # Reverse proxy support
  $ENV{MOJO_REVERSE_PROXY} = 1;
  $self->hook('before_dispatch' => sub {
    my $self = shift;
    
    if ( $self->req->headers->header('X-Forwarded-Host')) {
      my $prefix = shift @{$self->req->url->path->parts};
      push @{$self->req->url->base->path->parts}, $prefix;
    }
  });
}

MOJO_REVERSE_PROXYの設定

環境変数のMOJO_REVERSE_PROXYを1に設定する必要があります。Mojolicious 5.0からは、X-Forwarded-Protoがあれば、自動的に認識されるようになっています。

$ENV{MOJO_REVERSE_PROXY} = 1;

(これを設定しても、元のアプリには何の影響もでません。)

フックを使ってリバースプロキシ経由のときのベースパスを書き換える

正しくディスパッチするために、/gitwebliteの部分が、ベースパスだということを教えてあげないといけません。

$self->hook('before_dispatch' => sub {
  my $self = shift;
  
  if ( $self->req->headers->header('X-Forwarded-Host')) {
    my $prefix = shift @{$self->req->url->path->parts};
    push @{$self->req->url->base->path->parts}, $prefix;
  }
}

これでホスト名とポート番号が正しくurl_forやredirect_toで出力されるようになります。この記述を行っても試験環境でスタンドアロンでアプリケーションを実行するときでも、特に悪影響はありません。

SSLのページでHTTPページへのリンクを作成したい場合

httpsでアクセスした場合に、そのページの中にルート相対パス(/app1)でURLを指定した場合は、次のようなURLだと解釈されてしまいます。https://yourhost.com/app1

SSLのページでHTTPのページへのリンクを張りたい場合は次のようにするとよいです。

URL: <a href="<%= url_for('/foo')->to_abs->scheme('http') %>">Foo</a>

url_forはMojo::URLオブジェクトを返却するので、絶対URLに変換してから、スキーマをhttpに変換します。

url_for('/foo')->to_abs->scheme('http')

このようにすることでSSLのページから簡単にhttpへのリンクを張ることができます。

HTTPページでSSLのページへのリンクを作成したい場合

HTTPページでSSLのページへのリンクを作成したい場合は次のようにするとよいです。

URL: <a href="<%= url_for('/foo')->to_abs->scheme('https') %>">Foo</a>

url_forはMojo::URLオブジェクトを返却するので、絶対URLに変換してから、スキーマをhttpsに変換します。

url_for('/foo')->to_abs->scheme('https')

このようにすることでSSLのページへのリンクを張ることができます。

url_forメソッドでSSHのURLを生成する

url_forメソッドはHTTPのURLだけではなくて、URLの構造を持つものであれば、何でも作ることができます。

では次のようなSSHのURLを作ってみましょう。

ssh://kimoto@somehost.com:20000/foo/bar.git

次のようにします。

my $url = url_for('/foo/bar.git')->to_abs->scheme('ssh')
  ->userinfo('kimoto')->host('somehost.com')->port(20000);

まずto_absメソッドで絶対URLに変換してから、schemeメソッドでプロトコルを、userinfoメソッドでユーザー名を、hostメソッドでホスト名、portメソッドでポート番号を設定します。

関連情報