[Symfony] [Routing] ルートの host はなるべく設定した方がいいよと言うお話
この記事は Symfony Advent Calendar 2014 の12日目の記事です。
当初はFormについて何か書く予定でしたが、どうしても長くなってしまうのでRoutingについて書こうと思います。
SymfonyのRoutingでは、YAML, XML, Annotationなど様々な形式でルートを設定する事ができますが、皆さんは何で設定していますか?僕はYAMLとAnnotationの両方をよく使います。
Webアプリケーションでは、エンドユーザ向けやシステム管理者向けと言った、複数のサイト構成を取る事が多いと思います。その場合、サイト毎の共通設定をYAML、ページ毎の設定をAnnotationで設定します。
# app/config/routing.yml # エンドユーザ向け frontend: host: app.com prefix: / resource: @AppBundle/Controller/Frontend/ # システム管理者向け backend: host: app.com prefix: /administration/ resource: @AppBundle/Controller/Backend/
<?php // src/AppBundle/Controller/Frontend/DefaultController.php // ... /** * @Route("/") */ class DefaultContrller extends Controller { /** * http://app.com/ のコントローラ * * @Route("/", name="app_frontend_default_top") */ public function topAction(Request $request) { // ... } } // src/AppBundle/Controller/Backend/DefaultController.php // ... /** * @Route("/") */ class DefaultContrller extends Controller { /** * http://app.com/management/dashboard のコントローラ * * @Route("/dashboard", name="app_backend_default_dashboard") */ public function dashboardAction(Request $request) { // ... } }
本題に入りますが、皆さんは普段ルートの host
ディレクティブを設定していますか?勿論、サイト毎に異なるドメインを使う場合はしていると思いますが、上記のように同じドメインを使う場合はしていない方もいると思います。
そもそもホストルーティングはHTTPサーバがやってくれるので、URLマッチングでは必要ありません。ですが、これらの設定はURL生成でも使われる為、きちんと設定していないと問題が起きる事があります。
例えば、RouterItnerface::generate()
の第3引数を true
にすると絶対URLを生成する事ができますが、試しに先ほどの host
をコメントアウトして試してみましょう。
コントローラ:
<?php echo $this->get('router')->generate('app_backend_default_dashboard', [], true); // FrameworkBundleのControllerを継承していればショートカットが使える echo $this->generateUrl('app_backend_default_dashboard', [], true);
Twigテンプレート:
{{ url('app_frontend_default_top') }}
上記3パターン、いずれも結果はこうなります:
http://app.com/administration/dashboard
URLは正しく生成されました。一見問題が無いように見えますが、コンソールアプリケーションではどうでしょうか?
<?php // src/AppBundle/Command/EchoAbsoluteUrlCommand.php // ... class EchoAbsoluteUrlCommand extends ContainerAwareCommand { protected function execute(InputInterface $input, OutputInterface $output) { $router = $this->getContainer()->get('router'); $output->writeln($router->generate('app_backend_default_dashboard', [], true)); } }
結果は次のとおりです:
http://localhost/administration/dashboard
ホストが localhost
になってます。エンドユーザ向けに配信されるメールに使われるURLがこうなってしまったら大変ですね。
ではなぜコンソールだと正しいURLが作られないのでしょうか?答えはドキュメントに書いてあります。
絶対 URL のホスト部分には、現在の Request オブジェクトのホストが使用されます。ホスト情報は PHP のサーバー情報から自動的に検出されるため、コマンドラインから実行するスクリプトの場合は、RequestContext オブジェクトで明示的にホストを指定してください。
ルーティング | Symfony2日本語ドキュメント
具体的には、HttpKernelのonKernelRequestイベントでRequestContextにサーバ情報が渡されます。そして、このイベントはコンソールでは実行されない為、ホストが localhost
となってしまった訳です。
ドキュメントに沿って、URL生成前にRequestContextにホストを指定してやれば正しく動作するのですが、実はRouterは対象のルートにホストが設定されている場合はそちらの値を使用します。
なので、最初から host
ディレクティブの設定をした方が簡単かつ安全です。そしてこの値には、DIコンテナのパラメータが使えるので、環境毎に異なる設定をする事も可能となっています:
# app/config/parameters.yml parameters: # ... base_domain: app.com # app/config/routing.yml # エンドユーザ向け frontend: host: %base_domain% prefix: / resource: @AppBundle/Controller/Frontend/ # システム管理者向け backend: host: %base_domain% prefix: /administration/ resource: @AppBundle/Controller/Backend/
まとめ
- ルートのhostディレクティブはURLマッチングだけでなく、絶対URLの生成の時にも使われる
- 設定されてなければRequestContextの値を使う
- コンソールではRequestContextが正しく初期化されない
- なのでURLマッチングでは不要でもhostは設定しておいた方がいい