絵で見て理解する ASP.NET シリーズ:Routing の仕組み

さて、今回の「絵で見て理解する ASP.NET シリーズ」(※1)では、.NET Framework 3.5 SP1 で導入された Routing (System.Web.Routing)についてみていきたいと思います。

ASP.NET ではこの Routing を使用することにより、例えば

https://www.example.com/productlist.aspx?id=1214&category=toys&page=3

といったURLに対して、

https://www.example.com/toys/1214/3

といった別表記のURLにてアクセスすることが可能になります。

 

今回はこの Routing の仕組みを解説したいと思います。

まず最初に主要な登場人物と、物語風の処理のあらすじです。

登場人物:

Route 一家。Routing 情報を知っている Route オブジェクトがいっぱいいる一家。

clip_image002

 

MyHandler。リクエストに基づき Page(WebForm)を返す。Route 一家と仲が良い。

clip_image004

 

BuildManager。結構偉い人。MyHandler が返す Page は実は彼が作っている。

clip_image006

 

Page。MyHandler によって返されるオブジェクト。

clip_image008

 

物語風あらすじ:

ある日、とあるユーザーから ASP.NET 村に “https://www.example.com/toys/1214/3” への Request が入ります。

この Request を受け取った ASP.NET 村の村長は、村一の物知り一家 Route 家にこの Request を誰に任せるのかよいか訪ねます。

Route 家で Request を良く調べた結果、今回は MyHandler 君に処理を任せるのが一番だと判断し、それを伝えます。

村長は MyHanlder 君を呼び出し、Request の処理を依頼します。

MyHandler 君は Request をもとに BuildManager に Page の作成をお願いし、できあがった Page を村長に渡したのでした。

 

 

ということで、概略を理解いただいたうえで、実際の解説に入りたいと思います。

 

実際の処理概要:

最初に Routing を使用したい Web アプリケーションにおいて、Global.asax に、適切な Route 情報を登録しておきます。

RouteTable.Routes.Add(new Route

    (

    "{category}/{productID}/{page}",

    new MyHandler("~/productlist.aspx")

    )

);

このRoute 情報は複数登録を行うことができます。

Web アプリケーションに対して、ユーザー(Webアプリケーションの使用者)から Request が投げられると、Web アプリケーションは RouteTable(物語におけるRoute一家) の Route 情報を順番に確認し、Request にマッチする Route があれば、それに関連づいている Handler クラス(実際には IRouteHandler インターフェィスを実装した何らかのクラス)の GetHttpHandler メソッドを呼び出します。

clip_image010

 

Handler (IRouteHandler インターフェィスを実装した何らかのクラス) の GetHttpHandler メソッドでは Reqest に最適な Page オブジェクトを生成し、返します。

clip_image012

 

この Page の生成には、BuildManager の CreateInstanceFromVirtualPath メソッドを使用しています。

さて、GetHttpHandler メソッドの戻り値は、IHttpHandler インターフェィスを実装している必要がありますが、ここで生成される Page クラス(WebForm)は System.Web.UI.Pageを継承しており、この System.Web.UI.Page が IHttpHandler インターフェィスを実装しているので特に細工はいりません(※2)

BuildManager の CreateInstanceFromVirtualPath で Page オブジェクトを作成して、さっくり返してあげれば、あとは生成された Page オブジェクトが処理を継続します。

 

Request から必要な情報の取り出し:

以上で Routing の概略は終了ですが、実際には Request から必要な情報を取り出す処理(①)と、その情報を Pageに渡す処理(②)が必要です。

まず、①の情報の取り出しについてですが、この部分は Routing の基本的な仕組みとしてフレームワークで提供されています。

具体的には、Route 情報の登録の際に指定したパターン(赤字部分)

RouteTable.Routes.Add(new Route

    (

    "{category}/{productID}/{page}",

    new MyHandler("~/productlist.aspx")

    )

);

に基づいて、Routing のフレームワークが、RequestContext.RouteData にその情報を格納しています。

(パターンで指定された文字(たとえば “category” )をキーに、実際の Request で与えられた文字列を値として保持します)

 

これを取り出すのは、以下のようにします。

public IHttpHandler GetHttpHandler(RequestContext requestContext)

{

string category = requestContext.RouteData.Values["category"] as string;

……

}

もしくは GetRequiredString メソッドでもOKです。

public IHttpHandler GetHttpHandler(RequestContext requestContext)

{

string category = requestContext.RouteData.GetRequiredString("category");

……

}

これで Request から必要な情報を取り出せました。

 

次に、②の「Pageに渡す処理」です。

これについては、BuildManager に作成させる Page クラスを拡張することで対応します。

具体的には、IHttpHandlerインターフェイスを継承した新しい Handler インターフェイスを作成し、その中に値を保持するためのプロパティを追加します。

以下のような感じです。

interface IMyHttpHandler : IHttpHandler

{

string Category { get; set; }

string ProductID { get; set; }

string Page { get; set; }

}

 

これにあわせて Page の宣言を変更しましょう。

public partial class ProductList : System.Web.UI.Page, IMyHttpHandler

 

最後に、BuildeManager による Page インスタンス作成時に、今回追加を行ったプロパティへの値の設定のためのコードを追加します。

public IHttpHandler GetHttpHandler(RequestContext requestContext)

{

var page = BuildManager.CreateInstanceFromVirtualPath

(VirtualPath, typeof(Page)) as IMyHttpHandler;

page.Category = requestContext.RouteData.Values["category"] as string;;

……

}

 

これで Request (VirtualPath) に含まれている情報を Page に渡すことができるようになりました。

 

以上で今回の絵で見て理解する ASP.NET シリーズ、「Routing の仕組み」を終了します。

ありがとうございました。

 

謝辞:

今回のエントリは Routing について調べていたところ、以下の2つの素晴らしい記事に接し、それに触発されて書いたものです。

小山さんと小野さんにお礼申し上げます <(_ _)>

 

IIS/Windows サーバー徹底解説:ASP.NET ルーティング (ASP.NET Routing)

https://keicode.com/dotnet/aspnet-routing.php

どっとねっとふぁんBlog:ASP.NET ルーティングを実装する

https://dotnetfan.org/blogs/dotnetfanblog/archive/2008/09/18/2809.aspx

 

今回のエントリでは ASP.NET Routing の実際の使用方法、あるいは web.cofig の設定については触れていませんが、それについてはぜひ上記の記事を参照ください。

 

※1

思わずネタで書いてしまいましたが、当然そんなシリーズはなく、今回限りのエントリです。しかも、おもったより「絵」の少ないエントリですいません。。。

※2

最初に公開したときに、「Page で IHttpHandler を実装しましょう。といってもクラス宣言で、IHttpHandler を継承すればOKです」といった記述をしていましたが、小野さんから、「Page 自体が IHttpHandler を継承している」ので、「両方を継承する必要はないんじゃ」とコメントいただき、そのとおりですので書き換えました。スイマセン。