Razor Pages 實作 Ajax 呼叫
10 |
上回介紹過 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 怎麼寫 黑大的及時雨太棒了
# by Gain
您好,如果要用get傳值 $.get("Index?handler=ReadMsg").done(function (res) { $("#dvMsg").text(res); }); 裡面的Url要怎麼去定義呢? 還有後臺的OnGet方法裡面的參數要怎麼定義去接收呢? 謝謝。
# by Jeffrey
to Gain, 用GET+Handler的範例不多,這裡有一個:https://medium.com/@DomBurf/asp-net-core-2-0-razor-page-handlers-369116576b19
# by longer
使用Post下面方法,回傳出來的卻是整個頁面的html code?? public IActionResult OnPostReadMsg() { return Content("test"); }
# by Jeffrey
to longer,先確認呼叫 URL 是 Index?handler=ReadMsg,建議用瀏覽器 F12 工具看一下 Request 內容。
# by longer
你的提醒讓我想到是不有快取? 所以我把Ajax post url改成錯誤的ulr,直接出錯404,再回來試就正常,這種狀況讓人試到快抓狂 感謝你!
# by longer
您的提醒,我剛剛把網頁Ajax post url改成錯誤的ulr,直接出錯404,再回來試就正常,哪有這回事?難不成有快取?真試人試到快抓狂
# by Lauyea
黑大你好,我有個功能是:按鈕按下去就發出一個要求,去改資料表中的一個bool值。 我目前是用form+post去呼叫handler的方式去發要求,但我發現這樣還是會變成postback,他還是會重整整個頁面,讓我其他沒有拿出來的資料顯示null。 因為我不太熟悉AJAX,想問問有沒有什麼辦法,只發出一個簡單的要求,就可以改動資料表其中一個值,又不用重整頁面? 感謝百忙中抽空解惑!
# by Jeffrey
to Lauyea,這樣的需求建議用 JavaScript XHR 處理,你可以參考文章 $("#btnAjaxPost").click(...) 範例,done() 那一段會收到伺服器端傳回的內容,再用它來改動網頁表格的特定元素,但前題是你要懂一些 JavaScript 或 jQuery 才能勝任。有個逃避不學 JS 的做法是把要變動的部分切成 IFrame 嵌入一個只顯示數字/文字的特別 cshtml,需要更新時重新載入內嵌網頁(網頁其他部分不變),但建議還用 JS 解決才是王道。
# by Lauyea
了解了,看起來還是AJAX比較容易,如果希望達成類似效果還是避不開JS。 因為我是用Razor pages當作輕前端,JS程式碼的Debug一直都讓我很頭痛...而且之後.NET 的 Blazor如果可以實際應用,對於只擅長C#的我來說,容易Debug又能夠做出SPA的效果,感覺真的很棒XD 再次感謝黑大的幫忙!