上回介紹過 Razor Pages,是 ASP.NET Core 在 MVC 之外的簡便選擇, 概念上更貼近 WebForm 的簡單直覺,只需一個 .cshtml 加一個 .cshtml.cs 就可寫出動態網頁。

Razor Pages 的 GET 與 POST 伺服器端邏輯要寫成 Model 物件的 OnGet 與 OnPost 方法, 若不同按鈕要對映不同 POST 行為則可寫成 OnPostMethod1()、OnPostMethod2(), 前端再透過 asp-page-handler 註記 <button type="submit" asp-page-handler="Method1"><button type="submit" asp-page-handler="Method2"> 指向各自的 Post 端邏輯。 呼叫時會 URL 多出 ?handler=Method1、?hadler=Method2 參數,Razor Pages 就是依據它 決定該由 OnPostMethod1() 還是 OnPostMethod2() 處理。

但如果是要透過 jQuery.get()、jQuery.post() 呼叫 Razor Pages 怎麼辦?

洞悉 Razor Pages 是透過 ?handler 決定呼叫哪一段程式,不難推導只要在 AJAX URL 加上 ?handler=MethodName, 伺服器端再寫個 OnGetMethodName() 或 OnPostMethodName() 傳回內容便大功告成了。

來試試。

開個 ASP.NET Core 新專案,在 Index.cshtml 放入三個 <button>, 分別測試標準的 <form> 送出、AJAX GET 呼叫以及 AJAX POST 呼叫:

@page
@model IndexModel
@{
    Layout = null;
    ViewData["Title"] = "Home page";
}
<html>
<body>
    <form method="post">
        <div id="dvMsg">@Model.Message</div>
        <button type="submit" asp-page-handler="Refresh">NewGuid (Postback)</button>
        <button type="button" id="btnAjaxGet">NewGuid (Ajax GET)</button>
        <button type="button" id="btnAjaxPost">NewGuid (Ajax POST)</button>
    </form>
    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script>
        $("#btnAjaxGet").click(function () {
            //忽略例外處理
            $.get("Index?handler=ReadMsg").done(function (res) {
                $("#dvMsg").text(res);
            });
        });
        $("#btnAjaxPost").click(function () {
            $.ajax({
                method: "post",
                url: "Index?handler=ReadMsg",
                error: function (xhr, status, err) {
                    alert(err);
                }
            }).done(function (res) {
                $("#dvMsg").text(res);
            });
        });
    </script>
</body>
</html>

Index.cshtml.cs 部分也很簡單,只需三個方法:

  • OnPostRefresh() 處理標準的表單送出動作
  • OnGetReadMsg() 處理 jQuery.get() 取值
  • OnPostReadMsg() 則處理 jQuery.ajax() 送出的 POST 呼叫

程式碼如下:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System;

namespace Demo1.Pages
{
    public class IndexModel : PageModel
    {
        public string Message { get; set; } = DateTime.Now.Ticks.ToString();
        public void OnGet()
        {
            
        }

        public void OnPostRefresh()
        {
            Message = DateTime.Now.Ticks.ToString() + ":PostBack";
        }

        public IActionResult OnGetReadMsg()
        {
            return Content(DateTime.Now.Ticks.ToString() + ":AjaxGet");
        }

        public IActionResult OnPostReadMsg()
        {
            return Content(DateTime.Now.Ticks.ToString() + ":AjaxPost");
        }
    }
}

實測一下,表單送出與 AJAX GET 成功,但是 AJAX POST 得到 Bad Request 錯誤。

原因是 ASP.NET Core 內建 XSRF/CSRF (Cross-Site Request Forgery,跨站請求偽造) 防護功能, 會阻擋來路不明的 POST 請求。其原理是在 Razor Pages 網頁加入隱藏欄位 __RequestVerificationToken,內含類似通關密碼的一串編碼, 發送 POST 時必須附上讓伺服器端檢查真偽,若未提供或驗證未過便判定呼叫不合法回傳 HTTP 400 Bad Request。

因此在做 AJAX POST 呼叫時,我們要在 Request Header 附上 __RequestVerificationToken 內容,伺服器則要調整設定通知 ASP.NET Core 從 Header 取出驗證 Token 檢查。

我們先修改 JavaScript 端,送出 POST 請求前,由<input type="hidden" name="__RequestVerificationToken">取出防偽 Token, 以 X-CSRF-TOKEN 為名加入 Request Header:

$("#btnAjaxPost").click(function () {
    $.ajax({
        method: "post",
        url: "Index?handler=ReadMsg",
        //加上X-CSRF-TOKEN header
        beforeSend: function (xhr) {
            xhr.setRequestHeader("X-CSRF-TOKEN",
                $('input:hidden[name="__RequestVerificationToken"]').val());
        },
        error: function (xhr, status, err) {
            alert(err);
        }
    }).done(function (res) {
        $("#dvMsg").text(res);
    });
});

伺服器端則要修改 Startup.cs,宣告我們會在 X-CSRF-TOKEN Request Header 傳送防偽 Token:

public void ConfigureServices(IServiceCollection services)
{
    //... 省略 ...
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    //宣告 AJAX POST 使用的 Header 名稱
    services.AddAntiforgery(o => o.HeaderName = "X-CSRF-TOKEN");
}

搞定收工!

Tutorial of how to implement AJAX call in Razor Pages.


Comments

# by overing

最近正好也在研究 Razor Pages 怎麼寫 黑大的及時雨太棒了

Post a comment


5 - 5 =