ABP官方文件(三十八)【AJAX API】
6.6 ABP表現層 - AJAX API
6.6.2.1 AJAX操作問題
現代的應用經常會使用AJAX,尤其是單頁應用,幾乎是和伺服器通訊的唯一手段,執行AJAX通常會有以下步驟:
基本上:為了執行一個AJAX呼叫,首先你要在客戶端提供一個可供請求的URL,選取提交資料和一個方法(GET,POST,PUT,DELETE)。
等待呼叫完成後,處理返回結果。當執行AJAX呼叫伺服器端的時候,可能會有錯誤(一般是網路錯誤)。當然也有可能是伺服器端產生了一些錯誤,對於這些錯誤會,伺服器會返回一個失敗的響應並且附上錯誤訊息給客戶端。
客戶端程式碼應該處理這些錯誤,並且可以選擇通知使用者(可以顯示一個錯誤對話方塊)。如果沒有錯誤且伺服器端返回了資料,客戶端必須處理它。還有你應該限制頁面的某個區域(或者整個頁面),並顯示一個忙碌的指示直到AJAX操作完成。
伺服器端在得到請求後執行伺服器端程式碼,捕獲異常並返回一個有效的響應給客戶端。在錯誤情況下,可以選擇傳送錯誤訊息給客戶端。如果是驗證錯誤,伺服器端可以新增驗證錯誤的驗證資訊。在成功情況下,可以傳送返回值給客戶端。
6.6.2.2 ABP的方式
由於使用 abp.ajax 函式對AJAX呼叫進行了封裝, 所以ABP能自動化這些步驟。下面是一個AJAX呼叫示例:
var newPerson = {
name: 'Dougles Adams',
age: 42
};
abp.ajax({
url: '/People/SavePerson',
data: JSON .stringify(newPerson)
}).done(function(data) {
abp.notify.success('created new person with id = ' + data.personId);
});
abp.ajax得到 options 作為物件。你可以傳遞任何有效的jQuery的 $.ajax 函式中的引數。有一些預設引數:dataType 是 json,type是 POST,還有 contentType是 application/json(在傳送資料到伺服器端之前,我們需要呼叫 JSON.stringify 將指令碼物件轉換為JSON字串)。通過對apb.ajax傳遞options可以覆蓋預設值。
abp.ajax返回promise。因此,你可以寫這些處理函式:done,fail,then等等。在這個例子中,我們對 PeopleController’s SavePerson action 傳送了一個簡單的AJAX請求。在 done 處理函式中,我們對新建立的person取得了它的主鍵id並且顯示了建立成功的通知。讓我們看看 MVC Controller:
public class PeopleController : AbpController
{
[HttpPost]
public JsonResult SavePerson(SavePersonModel person)
{
//TODO: 儲存新建立的person到資料庫並且返回person的id
return Json(new {PersonId = 42});
}
}
正如你猜測的 SavePersonModel 包含了Name和Age屬性。SavePerson 被標記為 HttpPost 特性,因為abp.ajax預設方法是POST。通過返回了匿名物件簡化了方法實現。
這個看上去很簡單直白,但是ABP在背後做了很多重要的處理。讓我們深入瞭解一下:
6.6.2.3 AJAX 返回訊息
即使我們直接的返回了一個帶有PersonId = 2 的物件,ABP也會使用 MvcAjaxResponse 物件來包裝它。事實上AJAX響應返回的內容應該像下面一樣:
{
"success": true,
"result": {
"personId": 42
},
"error": null,
"targetUrl": null,
"unAuthorizedRequest": false,
"__abp": true
}
在這裡所有的屬性都是駝峰命名的(因為這在JavaScript中是慣例),即使在服務端程式碼中是PascalCased的。下面解釋一下所有的欄位:
success:boolean型別的值(true或者false),用來表示操作的成功狀態。如果是ture,abp.ajax會解析該promise並且呼叫 done 函式。如果是false(在方法被呼叫的時候,如果有個異常被丟擲),它會呼叫 fail 函式並且使用 abp.message.error 函式顯示 error 訊息。
result:控制器的action的實際返回值。如果success是ture並且伺服器傳送了返回值那麼它才是有效的。
error:如果success是false,這個欄位是一個包含 message和details 欄位的物件。
targetUrl:如果需要的話,這提供了一種可能性:伺服器端傳送一個URL到客戶端,使客戶端可以重定向到其它的URL。
unAuthorizedRequest:這提供了一種可能性:伺服器端傳送通知給客戶端該操作未被授權,或者是未認證使用者。如果該值是true,那麼abp.ajax會 reloads 當前的頁面。
__abp:通過ABP包裝響應返回的特殊簽名。你自己不需要用到它,但是abp.ajax會處理它。
這種格式的物件會被 abp.ajax 函式識別且處理。abp.ajax會得到控制器的實際返回值(一個帶有personid屬性的物件),如果沒有錯誤的話,那麼你會在done函式中處理返回值。
6.6.2.4 處理錯誤
正如上面所述,ABP在伺服器端處理異常,並且返回一個帶有錯誤訊息的物件。如下所示:
{
"targetUrl": null,
"result": null,
"success": false,
"error": {
"message": "An internal error occured during your request!",
"details": "..."
},
"unAuthorizedRequest": false,
"__abp": true
}
正如你看到的,success是false 並且 result是null。abp.ajax處理這個物件,並且使用abp.message.error函式來顯示錯誤訊息給使用者。如果你的伺服器端程式碼丟擲了 UserFriendlyException 型別的異常。它會直接的顯示異常資訊給使用者。否則,它會隱藏實際的錯誤(將錯誤寫入日誌),並且顯示一個標準的“伺服器內部錯誤…”資訊給使用者。所有的這些都是ABP自動處理的。
你可能想為某個特別的AJAX呼叫禁止顯示訊息,那麼新增 * abpHandleError: false* 到 abp.ajax的options。
HTTP狀態碼
在異常發生的時候,ABP會返回給定的HTTP狀態碼:
401:未經身份驗證的請求(沒有登入,但是伺服器端需要身份驗證);
403:未授權的請求;
500:所有其它型別的異常。
6.6.2.5 WrapResult和DontWrapResult特性
使用 WrapResult和DontWrapResult 特性,可以對控制器的某個action或者所有的action來控制包裝。
ASP.NET MVC 控制器
如果返回的型別是 JsonResult(或者Task\
public class PeopleController : AbpController
{
[HttpPost]
[WrapResult(WrapOnSuccess = false, WrapOnError = false)]
public JsonResult SavePerson(SavePersonModel person)
{
//TODO: 儲存新建立的person到資料庫並且返回person的id
return Json(new {PersonId = 42});
}
}
作為一個快速開發方式,我們只能使用 [DontWrapResult] 特性在這個相同的示例上。
你可以在啟動配置裡面改變這個預設的行為(使用 Configuration.Modules.AbpMvc()…)。
ASP.NET Web API 控制器
如果action被成功執行,ABP 不會預設包裝 Web API Action的返回結果。如果需要的話,你可以新增WrapResult特性到action或者控制器上。但是它會 包裝異常。
你可以在啟動配置裡面改變這個預設的行為(使用 Configuration.Modules.AbpWebApi()…)。
動態Web API層
預設 ABP會 包裝 動態Web API層的所有方法。你可以在你應用服務的介面上使用 WrapResult和DontWrapResult 特性來改變這個行為。
你可以在啟動配置裡面改變這個預設的行為(使用 Configuration.Modules.AbpWebApi()…)。
ASP.NET Core 控制器
ABP會自動包裝JsonResult,ObjectRes以及那些沒有實現IActionResult物件。詳情請查閱ASP.NET Core文件。
你可以在啟動配置裡面改變這個預設的行為(使用 using Configuration.Modules.AbpAspNetCore()…)。
6.6.2.6 動態Web API層
雖然ABP提供了一種呼叫Ajax的簡單機制,但是在真實世界的應用中,為每個Ajax呼叫編寫javascript函式是很經典的。例如:
//建立一個抽象了Ajax呼叫的function
var savePerson = function(person) {
return abp.ajax({
url: '/People/SavePerson',
data: JSON.stringify(person)
});
};
//建立一個新的 person
var newPerson = {
name: 'Dougles Adams',
age: 42
};
//儲存該person
savePerson(newPerson).done(function(data) {
abp.notify.success('created new person with id = ' + data.personId);
});
這是一個最佳實踐,但是對每個AJAX呼叫函式都這樣做,那是耗時且乏味的。對於應用服務和控制器,ABP能夠自動的生成這些函式。