shuhelohelo’s blog

Xamarin.Forms多めです.

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 というページハンドラの場合、MyActionAjaxで宛先の指定に使われるページハンドラ名である。

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();
                }
            });
        }