.NET Core 3.0がリリースされて、ASP.NET Core Blazorなども盛り上がっている様子です。
しかし残念ながら私のASP.NET Coreの知識はRazor Pagesの登場時点で止まっています。
最新技術にキャッチアップするために、後れを取ったところから確認しなおす必要があります。
環境
- Visual Studio 2019 16.4.0 Preview 1.0
- .NET Core SDK 3.0.100
Page Handlerについて
例えば、次のようなRazorPage, PageModel, Modelを用意します。
<!-- Pages/Sample.cshtml --> @page "{handler?}" @model WebApplication2.Pages.SampleModel @{ ViewData["Title"] = "Sample"; } <h2>@ViewData["Title"].</h2> <h3>@Model.Message</h3> <form method="post" asp-page-handler="create"> <label asp-for="Person.Id"></label> <input asp-for="Person.Id" type="number"/> <br/> <label asp-for="Person.FirstName"></label> <input asp-for="Person.FirstName"/> <br /> <label asp-for="Person.LastName"></label> <input asp-for="Person.LastName"/> <br /> <input type="submit" value="Submit" /> </form>
// Pages/Sample.cshtml.cs [AutoValidateAntiforgeryToken] public class SampleModel : PageModel { public string Message { get; set; } [BindProperty] public Person Person { get; set; } public void OnGet() { Message = "Your application description page."; } public void OnPostCreate() { var str = @$"Id:{Person.Id}, FirstNaem:{Person.FirstName}, LastName:{Person.LastName}"; Message = str; }
// Models/Person.cs public class Person { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } }
RazorPageのform
内の属性method="post" asp-page-handler="create"
が、PageModelのハンドラーメソッドOnPostCreate()
との対応しています。
On{Verb}{HandlerName}(Async)
という構造になっています。
従来のASP.NET MVCのようなHTML出力がなされています。
(action="/Sample/create"
となっているのは、先頭で@page "{handler?}"
と指定したことによるものです。)
FormをSubmitしたときサーバーサイドでOnPostCreate()
が発火されます。
Person
プロパティには、[BindProperty]
指定がしてあるため、なんともイイ感じに入力した値が格納されています。
ちなみに、ハンドラーメソッドOnPostCreate()
にPerson型パラメータを与えてOnPostCreate(Person person)
などとしてもForm入力値は反映されません。
OnPostCreate([FromForm]Person person)
とすることで解消できますが、[BindProperty]
したプロパティにも同じ値が格納されるのでどちらかで良いかもしれません。
インテリセンスを効かせたい
もちろん、これはこれで良いのですが一つ大きな不満があります。
<form method="post" asp-page-handler="create">
このasp-page-handler="create"
の"create"は、インテリセンスの守備範囲外なので、ハンドラーメソッド名と一致するかが実行時にしか分からないという点です。
そういえば、過去にも同じような不満を持っていました。
故に無理やり対応させます。
ダメだったものたち
ActionNameはダメでした。
//NG. ActionNameでは思い通りにいかなかった。 [ActionName("CreatePerson")] public void OnPostCreate() { var str = @$"Id:{Person.Id}, FirstNaem:{Person.FirstName}, LastName:{Person.LastName}"; Message = str; }
HttpPostはもっとダメでした。
//NG. HttpPostも思い通りに動かないし、RazorPageでは使えない旨の警告が出る。 [HttpPost("CreatePerson")] public void OnPostCreate() { var str = @$"Id:{Person.Id}, FirstNaem:{Person.FirstName}, LastName:{Person.LastName}"; Message = str; }
結局前回と同じような手段に
いよいよどうしようもないので、前回と同じ手段を取ります。
// Pages/Sample.cshtml.cs public void OnPostCreate() { var str = @$"Id:{Person.Id}, FirstNaem:{Person.FirstName}, LastName:{Person.LastName}"; Message = str; } public static class Handlers { public static readonly string OnPostCreate = "Create"; } }
<!-- Pages/Sample.cshtml --> @page "{handler?}" @model WebApplication2.Pages.SampleModel @using Handlers = WebApplication2.Pages.SampleModel.Handlers @{ ViewData["Title"] = "Sample"; } <h2>@ViewData["Title"].</h2> <h3>@Model.Message</h3> <form method="post" asp-page-handler="@Handlers.OnPostCreate"> <label asp-for="Person.Id"></label> <input asp-for="Person.Id" type="number"/> <br/> <label asp-for="Person.FirstName"></label> <input asp-for="Person.FirstName"/> <br /> <label asp-for="Person.LastName"></label> <input asp-for="Person.LastName"/> <br /> <input type="submit" value="Submit" /> </form>
RazorPage側でインテリセンスを効かせるという当初の目的は達成できますが、この方法も根本的な解決にはなっていません。
Handlers.OnPostCreate
の値とハンドラーメソッドOnPostCreate
のハンドラー名が一致していることを単体テストなどで保証しないといけない- テストケースを自動的に作成するためには、
Handlers.OnPostCreate
の名前とハンドラーメソッドOnPostCreate
の名前も一致していることが必要 - RazorPageで
Handlers.OnPostCreate
の存在をガン無視したら何の価値もない
と言ったところでしょうか。
PageHanlderは便利なのかもしれませんが、実行時までミス発見が遅れるのは勘弁願いたいですね。
それと少ないかもしれませんが、パスに-
(ハイフン)などを含めたい場合なども、この場合はどうすることもできません。
あと、WebForms感がそこはかとなくただよいます。