Razor PagesでAjaxでPostする
Razor Pagesではページハンドラー(MVCでのアクションメソッド)をAjaxから直接呼び出せるため、別途MVCのWebAPIとして作成する必要はなく、ページモデル(cshtml.cs)内に書くことができる。
以下の例ではJSONデータを返すだけのシンプルなものだが、画像を返すのであればおそらくFileStreamResultを返せばよい。
ページモデル側で以下のようにプロパティとOnPostMyActionページハンドラーを用意する。
[BindProperty] public MyClass MyClassData { get; set; } [BindProperty] public string MyString { get; set; } public IActionResult OnPostMyAction() { return new JsonResult(new { Result = this.MyClassData}); }
cshtml側で送信ボタンと、PostするAjaxを用意する
@Html.AntiForgeryToken() <button onclick="postData()">送信</button> @section Scripts{ <script> function postData() { $.ajax({ type: "POST", beforeSend: function (xhr) { xhr.setRequestHeader("XSRF-TOKEN", $('input:hidden[name="__RequestVerificationToken"]').val()); }, url: "?handler=MyAction", data: { myClassData: { Name:"hello" }, myString:"hello2" }, success: function (data) { console.log(data) calendar.refetchEvents(); onCloseModal(); } }); } </script> }
ボタンを押すとpostData()関数が実行され、myClassDataとmyStringにデータがセットされてPostされる。
ASP.NET CoreではXSRF対策がデフォルトで有効になるため、リクエストにはXSRFトークンをヘッダーにセットしておく必要がある。 そのため、XSRFトークンを保持するhidden inputを埋め込んでおく必要がある。
@Html.AntiForgeryToken()
この値を以下の部分でヘッダーにセットしている。
beforeSend: function (xhr) { xhr.setRequestHeader("XSRF-TOKEN", $('input:hidden[name="__RequestVerificationToken"]').val()); },
XSRFトークンのキー名(ここでは”XSRF-TOKEN”)を設定しておく必要があり、これをprogram.csで以下のように指定しておく。
builder.Services.AddAntiforgery(options => { options.HeaderName = "XSRF-TOKEN"; });
POST先は以下のように?handler={ページハンドラ名}
とする。ここでいうページハンドラ名とはページモデルで定義したページハンドラーからOnPost
を削除(もしAsync
が末尾についていればそれも除く)した文字列である。例えばOnPostMyActionAsync
というページハンドラの場合、MyAction
がAjaxで宛先の指定に使われるページハンドラ名である。
url: "?handler=MyAction"
Ajaxで送信するデータは、プロパティ名を指定して以下のようにする。
data: { myClassData: { Name:"hello" }, myString:"hello2" },
これでページモデル側の対応するプロパティに値が入る。
おまけ
Postするデータを構成するときに、プロパティ名をハードコーディングしたくない気がする。
その場合は以下のようにnameofでプロパティ名を指定することもできる。が、可読性が下がる気がする。
function postData() { $.ajax({ type: "POST", beforeSend: function (xhr) { xhr.setRequestHeader("XSRF-TOKEN", $('input:hidden[name="__RequestVerificationToken"]').val()); }, url: "?handler=MyAction", data: { @nameof(Model.MyClassData): { @nameof(Model.MyClassData.Name): "hello" }, @nameof(Model.MyString): "hello2" }, success: function (data) { console.log(data) calendar.refetchEvents(); onCloseModal(); } }); }