URL書き換えを行うでは特定のURLに対して書き換えを行う処理をIHttpModuleを実装したクラスで行う処理を掲載しました。

今回はさらに一般化して、構成ファイルで指定した正規表現に一致するURLがリクエストされたときに設定された値で書き換えを行うようにしてみます。

確認環境

  • 動作環境 Windows2003 上のIIS6.0, Visual Studio 2008 の組み込みコンテナ
  • 実装環境 Visual Studio 2008 Professional 英語版
  • .NET 3.5

1.作成するカスタムセクション

URL書き換えを指定するカスタムセクションの設定サンプルは以下のようになります。

rewritePathカスタムセクションで書き換えパターンを設定するようにします。rewritepathExpression要素内で、書き換えを行うパターンを複数登録します。targetPathがリクエストでマッチするかチェックする正規表現パターンです。rewritePathがマッチした内容を置換するのに使用するパターンです。正規表現パターンは.NET Frameworkで使用される正規表現を使用します。また、正規表現パターンには暗黙で、先頭にアプリケーションパスが設定されるものとします。つまり、サンプルでnameがFrontPageとなっている書き換えパターンはURLリクエストが/ComponentGeek/FrontPageに一致する場合は、/ComponentGeek/Default.aspxに置換するという意味になります。また、showArticleで設定してあるようにtargetPathで指定する正規表現にキーワードを設定しておき、rewritePathでキーワードの値(${ID}等)を設定することができます。

 <rewritePath>
    <rewritepathExpressions>
      <add name="FrontPage" targetPath="FrontPage$" rewritePath="Default.aspx"/>
      <add name="Contact" targetPath="Contact$" rewritePath="Contact.aspx"/>
      <add name="Categories" targetPath="Categories$" rewritePath="ShowCategories.aspx" />
      <add name="ShowArticle" targetPath="ShowArticle/(?&lt;ID&gt;.*)\.aspx" rewritePath="ShowArticle.aspx?ID=${ID}" />
    </rewritepathExpressions>
  </rewritePath>

 2.カスタム構成セクションの作成

Webサイトプロジェクトを作成し、App_Codeフォルダを作成し、新規クラスを作成します。新規作成したクラスはRewritePathConfigSection.csとします。作成されたファイルをクリックして表示されるプロパティウィンドウのビルドアクションがContentになっている場合は、Compileに変更することを忘れないでください。

RewritePathConfigSectionの設定は次のようになります。カスタムセクションについてよくわからない場合は、MSDNが本サイトで掲載している記事構成ファイルにカスタムセクションを定義し使用する を参照して下さい。

using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;

namespace ComponentGeek.Web.UI
{
    public class RewritePathConfigSection : ConfigurationSection
    {
        [ConfigurationProperty("rewritepathExpressions", IsRequired = true)]
        [ConfigurationCollection(typeof(RewritePathExpressionCollection))]  
        public RewritePathExpressionCollection RewritePathExpressionCollection
        {
            get { return (RewritePathExpressionCollection)base["rewritepathExpressions"]; }
            set { base["rewritepathExpressions"] = value; }
        }
    }
    public class RewritePathExpressionCollection : ConfigurationElementCollection
    {
        protected override ConfigurationElement CreateNewElement()
        {
            return new RewritePathExpressionElement();
        }

        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((RewritePathExpressionElement)element).Name;
        }
        public RewritePathExpressionElement this[int index]
        {
            get { return (RewritePathExpressionElement)BaseGet(index); }
            set
            {
                if (BaseGet(index) != null)
                {
                    BaseRemoveAt(index);
                }
                BaseAdd(index, value);
            }
        }
        public RewritePathExpressionElement this[string name]
        {
            get { return (RewritePathExpressionElement)BaseGet(name); }
        }
    }
    public class RewritePathExpressionElement : ConfigurationElement
    {
        /// <summary>
        /// 変換の名前を表す
        /// </summary>
        [ConfigurationProperty("name", IsRequired=true)]
        public string Name
        {
            get { return (string)base["name"]; }
            set { base["name"] = value; }
        }
        /// <summary>
        /// 書き換え対象のURLパスパターン
        /// </summary>
        [ConfigurationProperty("targetPath", IsRequired = true)]
        public string TargetPath
        {
            get { return (string)base["targetPath"]; }
            set { base["targetPath"] = value; }
        }
        /// <summary>
        /// 書き換え後のURLパス
        /// </summary>
        [ConfigurationProperty("rewritePath", IsRequired = true)]
        public string RewritePath
        {
            get { return (string)base["rewritePath"]; }
            set { base["rewritePath"] = value; }
        }
    }
}

3 IHttpModuleの作成

同じくApp_Code内でクラスを新規作成します。名前をShowArticleModule.csとします。下記のように編集します。BeginRequestイベントでリクエストのパスがカスタムセクションから読み取ったパスにマッチするかを判定します。PreRequestHandlerExecuteでは、書き換えが発生した場合にリクエストされたパスを書き戻す処理を行っています。掲載しているコードはマッチング処理等の効率は無視していますので、注意して下さい。

using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Text.RegularExpressions;

namespace ComponentGeek.Web.UI
{
    public class ShowArticleModule : IHttpModule
    {

        #region IHttpModule Members

        void IHttpModule.Dispose()
        {
        }

        void IHttpModule.Init(HttpApplication context)
        {
            context.BeginRequest += this.Application_BeginRequest;
            context.PreRequestHandlerExecute += this.Application_PreRequestHandlerExecute;
        }
        #endregion

        private static RewritePathConfigSection RewriteSection = (ComponentGeek.Web.UI.RewritePathConfigSection)ConfigurationManager.GetSection("rewritePath");

        private bool rewrited = false;
        private string originalPath = "";
        private void Application_BeginRequest(object sender, EventArgs e)
        {
            HttpApplication application = sender as HttpApplication;
            HttpContext context = application.Context;

            originalPath = context.Request.Url.PathAndQuery;
            rewrited = ShowArticleModule.RewriteIfMatch(context);
        }
        private void Application_PreRequestHandlerExecute(object sender, EventArgs e)
        {
            if (rewrited)
            {
                HttpApplication application = sender as HttpApplication;
                HttpContext context = application.Context;

                context.RewritePath(originalPath);
            }
        }
        private static bool RewriteIfMatch(HttpContext context)
        {
            string appPath = context.Request.ApplicationPath;
            if (!appPath.EndsWith("/"))
            {
                appPath += "/";
            }
            foreach (RewritePathExpressionElement elem in RewriteSection.RewritePathExpressionCollection)
            {
                Regex regex = new Regex("^" + appPath + elem.TargetPath, RegexOptions.Singleline);
                Match match = regex.Match(context.Request.Path);
                if (match.Success)
                {
                    string path = regex.Replace(context.Request.Path, appPath + elem.RewritePath);
                    context.RewritePath(path);
                    return true;
                }
            }
            return false;
        }
    }
}

4. 構成セクションに設定

カスタムセクションを構成ファイルに登録して、完了です。configSectionに設定する内容を下記に掲載しています。

<configSections>
    <!-- 省略 -->
    <section name="rewritePath" type="ComponentGeek.Web.UI.RewritePathConfigSection"/>
  </configSections>

カスタムセクション の登録後は、1.作成するカスタムセクションで掲載したようなカスタムセクションを設定します。正規表現のtargetPathで指定するURLが.aspxで終わらない場合、ワイルドカードスクリプトマップを追加する必要があります。IIS6の場合、仮想ディレクトリのプロパティ画面で、仮想ディレクトリタブの構成ボタンをクリックします。アプリケーションの構成ダイアログのマッピングタブからワイルドカードアプリケーションマップの追加ボタンをクリックし、実行可能ファイルに%windir%\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dllを指定します。64bitの場合は、%windir%\Microsoft.NET\Framework64\v2.0.50727\aspnet_isapi.dllです。IIS7.5の場合はハンドラーマッピングのワイルドカードスクリプトマップの追加から同様にマッピングを追加します。

以上で説明は完了です。誤り、修正点等がありましたらご連絡下さい。

追加の情報ですが、IIS7.0では汎用的なURL書き換えモジュールが提供されています。ASP.NETだけでなく、すべてのリソースファイルに対して書き換えを実現できるようなので、ご紹介します。Visual Studioの組み込みコンテナ上では使えないですが、要求にあっていれば一考の余地があると思います。

Using URL Rewrite Module
http://learn.iis.net/page.aspx/460/using-url-rewrite-module/