Asial Blog

Recruit! Asialで一緒に働きませんか?

symfonyとHTMLファイルを同居させた際のリンク切れ回避方法

カテゴリ :
バックエンド(プログラミング)
タグ :
Tech
PHP
symfony
井川です。こんにちは。

Webシステム構築をしていると、動的なシステムと静的なHTMLファイル(ドキュメントルート以下)を同時に配置することがあります。symfonyを使ってこのようなシステムやサイトを構築すると、HTMLファイルへの特定のリンクでは404エラーが発生してしまいます。今回はこれを回避する簡単な方法をご紹介します。

それらのファイルへアクセスでき、簡単に同居できます。しかしながら、リンクの書き方如何によっては、問題が発生します。例えば、/outline/index.htmlへリンクを貼りたい場合(webディレクトリ以下に配置)、次のようなリンクはsymfonyのモジュールとindex.htmlのどちらを意味するのでしょうか。

  1. <a href="/outline"></a>
  2. <a href="/outline/"></a>

リンク先でリクエストはsymfonyに渡され、symfonyではこれらのリンク先をモジュールと解釈します。そのため、仮に/outline/index.htmlが存在していても、404エラーをはいてしまいます。こういったリンクが膨大な場合や、新しいテンプレートファイルでリンク先を書き間違えた場合など、問題が発生する可能性は決して低くありません。

この問題を回避する方法の1つは、以下のようにフロントコントローラを作成することです(他の方法も多々あるとは思います)。このコントローラではsfFrontWebControllerを継承したクラスにcheckHtmlUriメソッドを加え、dispatchメソッドを上書きしています。

dispatchメソッドの中の変更点は、sfExceptionをキャッチしたブロック内部です。ここでは次のような処理を実行しています。

① 例外がsfError404Exceptionであるか確認する
② リクエストURLの最後がディレクトリである可能性を確認する
③ リクエストURL+index.htmlにファイルがあるか確認する
④ リクエストURL+index.htmlにリダイレクトする

checkHtmlUriメソッドでは②と③の処理を行い、リダイレクト先URLを設定します。dispatchメソッドでは、まず①の実行とcheckHtmlUriメソッドの呼び出しを行います。そして最終的に④のリダイレクトを行います。

  1. <?php
  2. /**
  3.  * フロントウェブコントローラ
  4.  * 
  5.  * @package    blog
  6.  * @subpackage controller
  7.  * @author     Kazushi IGAWA <kazushi@asial.co.jp>
  8.  * @date       2011.02.15
  9.  */
  10. class TestFrontWebController extends sfFrontWebController
  11. {
  12.   /**
  13.    * インデクスファイル名
  14.    */
  15.   const INDEX_FILE_NAME = 'index.html';
  16.   /**
  17.    * リダイレクト先
  18.    * @var string
  19.    */
  20.   private $redirectUrl = '';
  21.   /**
  22.    * リクエストを処理する
  23.    *
  24.    * @param  void
  25.    * @return void
  26.    */
  27.   public function dispatch()
  28.   {
  29.     try {
  30.       // reinitialize filters (needed for unit and functional tests)
  31.       sfFilter::$filterCalled = array();
  32.       
  33.       // determine our module and action
  34.       $request    = $this->context->getRequest();
  35.       $moduleName = $request->getParameter('module');
  36.       $actionName = $request->getParameter('action');
  37.       
  38.       if (empty($moduleName) || empty($actionName)) {
  39.         throw new sfError404Exception(sprintf('Empty module and/or action after parsing the URL "%s" (%s/%s).', $request->getPathInfo(), $moduleName, $actionName));
  40.       }
  41.       
  42.       // make the first request
  43.       $this->forward($moduleName, $actionName);
  44.     } catch (sfException $e) {
  45.       if ($e instanceof sfError404Exception && $this->checkHtmlUri($request)) {
  46.         $this->redirect($this->redirectUrl);
  47.       }
  48.       
  49.       $e->printStackTrace();
  50.     } catch (Exception $e) {
  51.       sfException::createFromException($e)->printStackTrace();
  52.     }
  53.   }
  54.   /**
  55.    * HTMLファイルが存在するか確認する
  56.    * 
  57.    * @param  sfWebRequest $request
  58.    * @return boolean true=静的ファイルあり, false=静的ファイルなし
  59.    */
  60.   protected function checkHtmlUri(sfWebRequest $request)
  61.   {
  62.     $uri  = $request->getUri();
  63.     
  64.     if ('/' !== substr($uri, -1)) {
  65.       return false;
  66.     }
  67.     
  68.     $filePath = sfConfig::get('sf_web_dir') . $request->getPathInfo() . self::INDEX_FILE_NAME;
  69.     
  70.     if (!file_exists($filePath)) {
  71.       return false;
  72.     }
  73.     
  74.     $this->redirectUrl = $uri . self::INDEX_FILE_NAME;
  75.     return true;
  76.   }
  77. }

後は、factories.ymlでコントローラを設定し、上記コントローラを組み込めば、当初の問題であった"/outline/"のようなリンクでも"/outline/index.html"へ遷移します。もちろん、symfonyの従来の機能(404エラーなど)はそのまま使用可能です。

  1. all:
  2.   controller:
  3.     class: TestFrontWebController

今回はコントローラのみを書き換える方針で作成しました。各クラスの役割を明確にするのであれば、checkHtmlUriメソッドやリダイレクト先はリクエストクラスに持たせた方がスッキリするかもしれません。

コメント

  • vector

    /outlineをモジュールとしてみるかどうかはrouting.yml次第じゃないですか?
    前提として
    default_index: { url: /:module, param: { action: index } }
    default: { url: /:module/:action/* }
    が設定してある場合を付け加えたほうがよろしいのではないでしょうか?

  • 井川数志

    ご意見ありがとうございます。
    今回取り上げた問題は、ご指摘のこととは異なるかと思います。

    扱っている問題は、webディレクトリ内のindex.htmlへのアクセスです。
    /outline/index.htmlへアクセスする際のリンクが/outline/である場合、
    symfonyでは404エラーとなります(outlineをモジュールとして設定していれば話は別ですが)。
    上記の方法は、この際に/outline/index.htmlへアクセスするためのものです。

    ちなみに、routing.ymlにdefault、default_indexを設定しようがしまいが、
    デフォルトのsymfonyでは上記のアクセスは常に404エラーとなります。
    (/outline/index.htmlへのリクエストがsymfonyへ渡されているため)

コメントフォーム



captcha_key

アシアルの会社情報

アシアル株式会社はPHP、HTML5、JavaScriptに特化したWebエンジニアリング企業です。ユーザーエクスペリエンス設計から大規模システム構築まで、アシアルメンバーが各々の専門性を通じてインターネットの進化に貢献します。

会社情報詳細

最近の記事