id:ladybug さんの以下のコメントで System.Web.Routing を調べた時に面白いプロパティを見つけたので試しました。
いつも、IgnoreRoute に contents/* や scripts/* を追加しなくてよいのだろうか?と悩みますが、追加しないものなのでしょうか?(最近だと、scripts/ は、CDN を使用するのでデバッグ用なのですが)
適当に静的ファイルだから ASP.NET の処理パイプラインに入らないから IgnoreRoute の指定がいらないんだろうとか思ってましたが、MSDN を調べたところしっかりと動作が説明されてました。
URL パターンに一致する物理ファイルが見つかった場合
既定では、ルーティングは、Web サーバー上の既存の物理ファイルにマップされる要求を処理しません。たとえば、http://server/application/Products/Beverages/Coffee.aspx という要求は、Products/Beverages/Coffee.aspx に物理ファイルが存在すると、ルーティングによって処理されません。この場合、定義されているパターン ({controller}/{action}/{id} など) に要求が一致しても、ルーティングによる処理は行われません。
ASP.NET ルーティング
すべての要求 (ファイルを指す要求であっても) をルーティングで処理するには、RouteCollection オブジェクトの RouteExistingFiles プロパティを true に設定して、既定の動作をオーバーライドします。この値を true に設定すると、定義されているパターンに一致するすべての要求がルーティングによって処理されます。
つまり、RouteCollection の RouteExistingFiles プロパティが true の時は静的なファイルへのリクエストでも ASP.NET MVC で処理できるということです。
そこでリクエストされた CSS ファイルをクライアントに返す前に、空白削除と改行削除という簡単な Minify 処理を行うサンプルコードを書いてみました。何はともあれ、まずはルーティング定義を弄るので Global.asax を編集します。
public static void RegisterRoutes(RouteCollection routes) { // 静的ファイルもルーティングの対象とする routes.RouteExistingFiles = true; // 今回は Scripts 以下のファイルは処理しない routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.IgnoreRoute("Scripts/{*pathInfo}"); // Content 以下にある拡張子が CSS のファイルへのリクエストは Content コントローラの Index へルーティング routes.MapRoute( "Stylesheet", "Content/{file}.css", new { controller = "Content", action = "Index" } ); routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults ); }
今回は JavaScript に対しては処理を行わないので IgnoreRoute に追加しておきます。そして CSS ファイルへのリクエストは ContentController の Index アクションへルーティングするようにしました。ではコントローラとアクションを見ていきます。
public class ContentController : Controller { // // GET: /Content/ public ActionResult Index(string file) { var path = Server.MapPath("~/Content/" + file + ".css"); var content = string.Join("", System.IO.File.ReadAllLines(path).Select(p => p.Trim())); return Content(content, "text/css"); } }
ちょっと手抜きしましたが、MapPath で実体のファイルパスを取得して File.ReadAllLines でファイルの全行を読み込み、そして LINQ を少しだけ使って空白削除をして結合しています。これを Content メソッドで MIME 指定して呼び出してあげるだけです。
それでは IE9 のネットワークタブでレスポンスを確認してみます。
Content/Site.css へリクエストを投げた応答本文は行表示が 1 しかないので 1 行で出力されていることがわかると思います。
これを応用すれば User-Agent を調べてキャリアごとに特化した CSS を出力することなどが出来ますね。