本節内容:
- AJAX操作相關問題
- ABP的方式
- AJAX 傳回資訊
- 處理錯誤
- HTTP 狀态碼
- WrapResult和DontWrapResult特性
- Asp.net Mvc 控制器
- Asp.net Web Api 控制器
- 動态Web Api層
- Asp.net Core 控制器
執行一個AJAX調用在現在的應用裡非常常見,尤其在SPAs(Single-Page Applications 單頁面應用)裡,它幾乎是唯一與伺服器通信的方式。一個AJAX調用由幾個重複的步驟組成:
在用戶端,基本上,javascript代碼應該提供一個URL,随意的一個資料和選擇一個方法(POST,GET...)來執行一個AJAX調用,它必須等待并處理傳回值,當向伺服器執行一個調用時,可能會出錯(通常網絡錯誤),或其它服務端錯誤,服務端傳回一個攜帶錯誤資訊的失敗的響應,用戶端應該處理這些或通知使用者(可顯示一個錯誤對話框),如果沒有錯誤,服務端發送一個傳回資料,用戶端也必須處理它。操作過程中,通常會屏蔽或整個螢幕并顯示一個AJAX正在操作的資訊,直到它完成。
服務端代碼擷取到一個請求,執行一些服務端代碼,捕獲任何的異常并傳回一個有效的傳回給用戶端。如果有出錯的情況,可能會發送錯誤資訊給用戶端,如果是一個驗證錯誤,伺服器可能會添加一個驗證問題。如果成功,可能會發送一個傳回值給用戶端。
ABP使用通過包裝了AJAX調用的abp.ajax,自動處理這些步驟,下面是一個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擷取一個可選的對象,你可以傳遞任何的參數(它會被jQuery的$.ajax方式驗證),此處有些預設:dataType:'json',type:'POST',contentType:'application/json'(是以,在發送到伺服器前,我們調用JSON.stringify把javascript轉換為JSON字元串),我們可以給abp.ajax傳遞options來覆寫這些預設.
abp.ajax傳回promise,是以,你可以寫done,fail,then....處理程式,在這個例子中,我們建立了一個簡單的AJAX請求,調用PeopleController的SavePerson操作,在done處理程式裡,我們擷取資料庫裡新建立的person的id并顯示一個成功的通知(檢視notification API)。我們看一下這個AJAX調用的MVC控制器:
public class PeopleController : AbpController
{
[HttpPost]
public JsonResult SavePerson(SavePersonModel person)
{
//TODO: save new person to database and return new person's id
return Json(new {PersonId = 42});
}
}
SavePersonModel包含Name和Age屬性,SavePerson标記為HttpPost,是以abp.ajax的預設方法為POST。我簡化方法的實作,隻傳回一個匿名對象。
這樣直截了當,但有些重要的東西ABP在背後進行了處理,讓我們深入細節...
即使我們傳回一個PersonId=2對象,ABP把它包裝成一個MvcAjaxResponse對象,AJAX響應實質上像下面這樣:
{
"success": true,
"result": {
"personId": 42
},
"error": null,
"targetUrl": null,
"unAuthorizedRequest": false,
"__abp": true
}
此處,所有屬性都是小駱峰式命名(因為這是javascript世界裡約定好的),即使它們在服務端是大駱峰式命名。讓我們解釋一下這些屬性:
- success:一個布爾值(true或false),訓示操作的是否成功,如果為true,abp.ajax解闆promise并調用done處理程式,如果為false(如果有方法調用中抛出異常),它調用fail處理程式,并使用abp.message.error函數顯示error資訊。
- result:傳回控制器裡操作的結果,如果success為true時伺服器發送一個傳回值後,它才可用。
- error:如果success為false,這個屬性包含一個錯誤明細資訊的對象。
- targetUrl:提供一個URL給服務端,在有需要的時候,把用戶端定向到這個URL。kid1412注(ABP版本為1.0-1.1):此處列出模闆生成的登入裡含有的targetUrl的示例代碼,另外迷惑人的是:生成的Login.cshtml裡包含登入的Form,和“登入”的submit按鈕,但它真正起作用的是Login.js裡的點選事件裡ajax,代碼如下:
-
[HttpPost] [DisableAuditing] public async Task<JsonResult> Login(LoginViewModel loginModel, string returnUrl = "", string returnUrlHash = "") { CheckModelState(); var loginResult = await GetLoginResultAsync( loginModel.UsernameOrEmailAddress, loginModel.Password, loginModel.TenancyName ); await SignInAsync(loginResult.User, loginResult.Identity, loginModel.RememberMe); if (string.IsNullOrWhiteSpace(returnUrl)) { returnUrl = Request.ApplicationPath; } if (!string.IsNullOrWhiteSpace(returnUrlHash)) { returnUrl = returnUrl + returnUrlHash; } return Json(new AjaxResponse { TargetUrl = returnUrl}); }
View Code
傳回的結果會在abp.jquery.js裡處理,并被重定向到targetUrl:
handleTargetUrl: function (targetUrl) { if (!targetUrl) { location.href = abp.appPath; } else { location.href = targetUrl; } },
- unAuthorizedRequest:服務端給用戶端一個通知:這個操作未被認證或使用者未被認證。如果為true,abp.ajax重新載入目前頁面。
- _abp:一個特殊的标志,表示響應是ABP包裝的,你不需要使用它,abp.ajax會處理它。
abp.ajax函數識别和處理這個傳回格式,如果不出錯,abp.ajax裡你的done處理程式擷取真正的控制器的傳回值(一個包含personId屬性的對象)。
如上所述,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裡。
ABP為異常傳回給定的狀态碼:
- 401為未認證的請求(使用者未登入,但服務端操作需要認證)。
- 403為未授權的請求。
- 500為所有其它類型的異常。
你可以通過為一個操作或控制器的所有操作使用WrapResult和DontWrapResult特性來控制包裝。
如果Asp.net Mvc 操作方法傳回類型為JsonResult(或異步的Task<JsonResult>),ABP預設地會進行包裝(如上所述),你可以使用WrapResult特性改變這種行為,如下所示:
public class PeopleController : AbpController
{
[HttpPost]
[WrapResult(WrapOnSuccess = false, WrapOnError = false)]
public JsonResult SavePerson(SavePersonModel person)
{
//TODO: save new person to database and return new person's id
return Json(new {PersonId = 42});
}
}
作為快捷方式,我們可以僅使用[DontWrapResult]來達到與此例相同目的。
你可以從啟動配置(使用Configuration.Modules.AbpMvc()...)改變這種預設行為。
ABP預設情況下不包裝成功的Web Api操作的結果,如果有需要,你可以添加WrapResult到操作或控制器上,但是預設包裝異常。
你可以從啟動配置(使用Configuration.Modules.AbpWebApi()...)改變這種預設行為。
ABP預設情況下包裝動态Web Api層的方法結果,你可以通過在你的應用服務接口上使用WrapResult和DontWrapResult特性來改這種行為。
ABP自動包裝JsonResult、ObjectResult和任何未實作IActionResult的結果,更多資訊檢視Asp.net Core 文檔。
你可以從啟動配置(使用Configuration.Modules.AbpAspNetCore()...)改變這種預設行為。
雖然ABP提供了一種簡單使用AJAX的機制,但在一個真實世界的應用裡,為每個AJAX調用寫一個javascript函數還是很典型的,例如:
//Create a function to abstract AJAX call
var savePerson = function(person) {
return abp.ajax({
url: '/People/SavePerson',
data: JSON.stringify(person)
});
};
//Create a new person
var newPerson = {
name: 'Dougles Adams',
age: 42
};
//Save the person
savePerson(newPerson).done(function(data) {
abp.notify.success('created new person with id = ' + data.personId);
});
為每個AJAX調用寫一個函數,這是一個好的實踐,但耗時且無趣, ABP可以自動地為應用服務和控制器生成這些類型的函數。
查閱動态Web Api層文檔擷取更多Web Api資訊,查閱Asp.net Core文檔擷取有關Asp.net Core內建資訊。
kid1412附:英文原文:http://www.aspnetboilerplate.com/Pages/Documents/Javascript-API/AJAX
kid1412聲明:轉載請把此段聲明完整地置于文章頁面明顯處,并保留個人在部落格園的連結:http://www.cnblogs.com/kid1412/(可點選跳轉)。