そろそろ ASP.NET Core MVC を本格的に使っていこうかと思っているので、これまで ASP.NET MVC 5 でよく使っていた Area を実際に Core MVC で使ってみることにします。
とは言っても、公式のドキュメントに書いてあることで十分ですが、URL ルーティングとの組み合わせの時に少し疑問があったので、その解消も同時に行います。
今の Visual Studio の Tooling だと Area を追加する方法が用意されていないので、手動でディレクトリを作成していく必要があるみたいです。正直かなりめんどくさいです。
ディレクトリ構造は ASP.NET MVC 5 の時と同じです。ビューの解決ルールも同じようです。
ただしビューの解決に関しては、Core MVC ではシンプルな方法でカスタマイズ可能になっています。詳細は省きますが AddRazorOptions で ViewLocationFormats を弄ると可能です。
ASP.NET MVC 5 の時は AreaRegistration が必要でしたが、Core MVC ではシンプルにコントローラに Area 属性を付けるだけになってます。全てに付けるのはちょっとめんどくさそうです。
using Microsoft.AspNetCore.Mvc; namespace AreasDemo.Areas.Admin.Controllers { [Area("Admin")] public class HomeController : Controller { public IActionResult Index() { return View(); } } }
これで Area の準備は出来てますが、アクセスするには URL ルーティングを追加する必要があります。
規約ベースで使う
大体の場合は規約ベースの URL ルーティングを追加するだけで問題ない場合が多いと思います。なので、最初にこっちの方法を紹介しておくことにします。
Core MVC では URL ルーティングのテンプレートに area を指定できるようになっているので、Area が存在する場合にはそのルーティングを優先するような書き方をするだけです。
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { app.UseMvc(routes => { routes.MapRoute( name: "areaDefault", template: "{area:exists}/{controller=Home}/{action=Index}/{id?}"); // Area 名を決めておきたい場合はこっち //routes.MapAreaRoute( // name: "adminArea", // areaName: "Admin", // template: "AdminSite/{controller=Home}/{action=Index}/{id?}"); routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); }
このルーティングを追加して、実際にアクセスするとちゃんと追加したページが表示されます。
これまでと同じ使い勝手なので簡単でした。
しかしアクション名を変えたり、ルーティングのテンプレートにパラメータを含めたい場合には、少し使い勝手が悪い挙動となるので、その場合には属性ベースで統一した方が良いと思いました。
属性ベースで使う
属性ベースのルーティングを使う場合には Area 用のルーティングは不要です。あまり情報が見つからなかったのですが、コントローラに Area 名を使う Route 属性を指定しておくと良さそうです。
2 重に定義してるみたいなので、ちょっと気持ち悪いですが我慢します。
using Microsoft.AspNetCore.Mvc; namespace AreasDemo.Areas.Admin.Controllers { [Area("Admin")] [Route("[area]")] public class HomeController : Controller { [Route("")] public IActionResult Index() { return View(); } [Route("sample-page")] public IActionResult Sample() { return View(); } } }
一部のアクションには特別なルーティングを定義していますが、この場合は area 名が prefix として必ず付くので意図したとおりの挙動となります。
具体的には以下のような URL でアクセス可能となります。
個人的には規約ベースのルーティングより属性ベースの方が好きなので、こっちを使っていく予定です。
URL の生成
Area を使っている場合の Tag Helpers を使った URL 生成は asp-area 属性に Area 名を指定するだけです。規約ベースや属性ベースに関係なく、意図したとおりの URL をちゃんと生成してくれます。
<a asp-action="Index" asp-controller="Home" asp-area="Admin">リンクのテスト 1</a> <a asp-action="Sample" asp-controller="Home" asp-area="Admin">リンクのテスト 2</a>
実際に生成された URL は以下の通りです。ちゃんと生成されています。
基本的には問題ないですが、URL は小文字にしておきたいので Routing のオプションを弄って小文字の URL を作成するようにします。
public void ConfigureServices(IServiceCollection services) { // Add framework services. services.AddRouting(options => { options.LowercaseUrls = true; }); services.AddMvc(); }
これでアクセスし直すと小文字の URL がちゃんと生成されていることが確認できます。
これで個人的に ASP.NET Core MVC と Area を使ってやりたいことは実現出来ました。ついでに属性ベースの URL ルーティングを使った時の疑問も解消出来たので、実際に使っていく予定です。
Core MVC に関係するのオプションは結構多いので、いつか時間があるときに調べたいです。