Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
さて、今回の「絵で見て理解する 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 オブジェクトがいっぱいいる一家。
MyHandler。リクエストに基づき Page(WebForm)を返す。Route 一家と仲が良い。
BuildManager。結構偉い人。MyHandler が返す Page は実は彼が作っている。
Page。MyHandler によって返されるオブジェクト。
物語風あらすじ:
ある日、とあるユーザーから 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 メソッドを呼び出します。
Handler (IRouteHandler インターフェィスを実装した何らかのクラス) の GetHttpHandler メソッドでは Reqest に最適な Page オブジェクトを生成し、返します。
この 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 を継承している」ので、「両方を継承する必要はないんじゃ」とコメントいただき、そのとおりですので書き換えました。スイマセン。