返回總目錄:ABP+AdminLTE+Bootstrap Table權限管理系統一期javascript
通過前幾節,咱們已經解決數據庫,模型,DTO,控制器和注入等問題.那麼再來看一下登陸邏輯.這裏算是前面幾節的一個初次試水.css
首先咱們數據庫已經有的相應的數據.html
模型和DTO已經建好,因此咱們直接在服務層添加Login方法就能夠了.java
在展示層添加Account控制器,注入IUserService接口,調用Login方法.jquery
而後添加視圖頁面.git
運行一下,看一下結果.github
除了頁面比較漂亮(哈哈),這些原本都沒有什麼好說的,直接上圖,web
這裏值得注意的是,咱們在建立下面的方法,在調用接口的的時候會報一個錯誤:web的App_Data/Logs/Logs.txt日誌文件中查看到打印到錯誤信息。ajax
public async Task<ListResultDto<UserInfoDto>> GetUsers() { var users = await _userRepository.GetAllListAsync(); return new ListResultDto<UserInfoDto>( users.MapTo<List<UserInfoDto>>() ); }
錯誤提示: Mapper not initialized. Call Initialize with appropriate configuration. If you are trying to use mapper instances through a container or otherwise, make sure you do not have any calls to the static Mapper.Map methods, and if you're using ProjectTo or UseAsDataSource extension methods, make sure you pass in the appropriate IConfigurationProvider instance.數據庫
意思是:映射器未初始化。 經過適當的配置調用初始化。 若是您嘗試經過容器或其餘方式使用映射器實例,請確保您沒有任何調用靜態Mapper.Map方法,若是您使用ProjectTo或UseAsDataSource擴展方法,請確保您傳入相應的IConfigurationProvider實例。
緣由:Mapper not initialized 映射未初始化
解決:在ApplicationModule類中增長依賴typeof(AbpAutoMapperModule)便可。
using System.Reflection; using Abp.Modules; using Abp.AutoMapper; namespace JCmsErp { [DependsOn(typeof(JCmsErpCoreModule), typeof(AbpAutoMapperModule))] public class JCmsErpApplicationModule : AbpModule { public override void Initialize() { IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly()); } } }
另外上面這個提示窗是否是很漂亮,登陸頁面的設計樣式都是本身寫的,本身這個彈窗,只是abp本身封裝.咱們來看一下具體的代碼.
abp.message.confirm( '請輸入密碼.', //確認提示 '肯定?',//確認提示(可選參數) function (isConfirmed) { if (isConfirmed) { //...delete user 點擊確認後執行 } } );
其實abp封裝不少js函數庫,這讓我不得不感嘆土牛(土耳其牛人)真是牛啊!看下圖左邊部分js文件
包括像abp.jquery.js,ABP服務端支持標準的ajax的請求/輸出。建議你們使用abp.jquery.js中提供的ajax請求方法,這個方法基於jquery的ajax方法,能夠自動處理服務端的異常信息,固然,若是你對js很熟練的話,也能夠根據本身的須要寫ajax。你也可使用jquery的ajax方法調用,可是須要設置一下默認請求參數,dataType 設置爲 'json', type 設置爲 'POST' and contentType 設置爲 'application/json,在發送請求時須要將js對象轉換成json字符串,和$.ajax同樣,你也能夠傳遞參數覆蓋abp.ajax的默認參數abp.ajax返回一個promise類型.Login上標記了 HttpPost 特性 abp.ajax默認以 POST 方式請求. 返回值被簡化成了一個匿名對象。
用於向用戶顯示對話框,展現消息或者獲得用戶的確認,ABP默認採用的sweetalert庫實現的對話框信息,使用時你須要引用sweetalert的樣式和js,在個人登陸頁面裏面已經引用了,而且引用abp.sweet-alert.js就可使用下列API了:
abp.message.info('some info message', 'some optional title'); abp.message.success('some success message', 'some optional title'); abp.message.warn('some warning message', 'some optional title'); abp.message.error('some error message', 'some optional title'); abp.message.confirm( 'User admin will be deleted.', //確認提示 'Are you sure?',//確認提示(可選參數) function (isConfirmed) { if (isConfirmed) { //...delete user 點擊確認後執行 } } );
設置一個半透明層,阻止點擊頁面元素,能夠覆蓋局部或者整個頁面,例子以下:
abp.ui.block(); //覆蓋整個頁面 abp.ui.block($('#MyDivElement')); //覆蓋指定元素,能夠把jquery對象做爲參數 abp.ui.block('#MyDivElement'); //或者直接使用選擇器參數 abp.ui.unblock(); //整個頁面解除覆蓋 abp.ui.unblock('#MyDivElement'); //指定元素解除覆蓋
UI Block API使用blockUI這個js庫來實現效果的,若是使用這個api須要在頁面引用blockUI的js庫和abp.blockUI.js文件。
UI Busy API 指示頁面繁忙的API,如ajax請求中:
abp.ui.block = function (elm) { if (!elm) { $.blockUI(); } else { $(elm).block(); } }; abp.ui.unblock = function (elm) { if (!elm) { $.unblockUI(); } else { $(elm).unblock(); } };
abp.ui.setBusy('#MyLoginForm');
abp.ui.clearBusy('#MyLoginForm');
這個主要是對瀏覽器console.log('...') 進行的包裝,能夠支持全部瀏覽器,你能夠經過設置abp.log.level來控制日誌輸出,和服務端同樣,如設置了abp.log.levels爲INFO時就不會輸出debug日誌了,你也能夠根據你的須要定製從新這些API。
abp.notify.success = function (message, title, options) { abp.log.warn('abp.notify.success is not implemented!'); }; abp.notify.info = function (message, title, options) { abp.log.warn('abp.notify.info is not implemented!'); }; abp.notify.warn = function (message, title, options) { abp.log.warn('abp.notify.warn is not implemented!'); }; abp.notify.error = function (message, title, options) { abp.log.warn('abp.notify.error is not implemented!'); };
和C#的string.Format同樣的用法
/* Formats a string just like string.format in C#. * Example: * abp.utils.formatString('Hello {0}','Tuana') = 'Hello Tuana' ************************************************************/ abp.utils.formatString = function () { if (arguments.length < 1) { return null; } var str = arguments[0]; for (var i = 1; i < arguments.length; i++) { var placeHolder = '{' + (i - 1) + '}'; str = abp.utils.replaceAll(str, placeHolder, arguments[i]); } return str; };
最後,固然咱們登陸頁面也能夠改一下.這樣就更簡潔了.
var url = "/Account/Login"; //構建要傳輸的參數對象 var newPerson = { userName:$("#userName").val(), password:$("#password").val() } }; //調用abp的ajax方法 abp.ajax({ url:url, data: JSON.stringify(newPerson) //轉換成json字符串 }).done(function (data) { abp.message.warn('用戶名或密碼錯誤!', '登陸失敗'); });
至此,咱們登陸邏輯,和JavaScript封裝模塊就所有完成了,其實abp提示窗還蠻好看的,你們也能夠借鑑一下在本身的項目裏面.
現代的應用常常會使用AJAX,尤爲是單頁應用,幾乎是和服務器通訊的惟一手段,執行AJAX一般會有如下步驟:
基本上:爲了執行一個AJAX調用,首先你要在客戶端提供一個可供請求的URL,選取提交數據和一個方法(GET,POST,PUT,DELETE)。
等待調用完成後,處理返回結果。當執行AJAX調用服務器端的時候,可能會有錯誤(通常是網絡錯誤)。固然也有多是服務器端產生了一些錯誤,對於這些錯誤會,服務器會返回一個失敗的響應而且附上錯誤消息給客戶端。
客戶端代碼應該處理這些錯誤,而且能夠選擇通知用戶(能夠顯示一個錯誤對話框)。若是沒有錯誤且服務器端返回了數據,客戶端必須處理它。還有你應該限制頁面的某個區域(或者整個頁面),並顯示一個忙碌的指示直到AJAX操做完成。
服務器端在獲得請求後執行服務器端代碼,捕獲異常並返回一個有效的響應給客戶端。在錯誤狀況下,能夠選擇發送錯誤消息給客戶端。若是是驗證錯誤,服務器端能夠添加驗證錯誤的驗證信息。在成功狀況下,能夠發送返回值給客戶端。
因爲使用 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在背後作了不少重要的處理。讓咱們深刻了解一下:
即便咱們直接的返回了一個帶有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函數中處理返回值。
正如上面所述,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會返回給定的HTTP狀態碼:
401:未經身份驗證的請求(沒有登陸,可是服務器端須要身份驗證);
403:未受權的請求;
500:全部其它類型的異常。
使用 WrapResult和DontWrapResult 特性,能夠對控制器的某個action或者全部的action來控制包裝。
若是返回的類型是 JsonResult(或者Task<JsonResult>),那麼ABP會默認包裝ASP.NET MVC action的返回結果。你可使用 WrapResult 特性來改變它。以下所示:
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()...)。
若是action被成功執行,ABP 不會默認包裝 Web API Action的返回結果。若是須要的話,你能夠添加WrapResult特性到action或者控制器上。可是它會 包裝異常。
你能夠在啓動配置裏面改變這個默認的行爲(使用 Configuration.Modules.AbpWebApi()...)。
默認 ABP會 包裝 動態Web API層的全部方法。你能夠在你應用服務的接口上使用 WrapResult和DontWrapResult 特性來改變這個行爲。
你能夠在啓動配置裏面改變這個默認的行爲(使用 Configuration.Modules.AbpWebApi()...)。
ABP會自動包裝JsonResult,ObjectRes以及那些沒有實現IActionResult對象。詳情請查閱ASP.NET Core文檔。
你能夠在啓動配置裏面改變這個默認的行爲(使用 using Configuration.Modules.AbpAspNetCore()...)。
雖然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可以自動的生成這些函數。
詳情請閱讀動態Web API層文檔和ASP.NET Core文檔。
當一些事情發生的時候,咱們喜歡顯示一些別緻的可以自動消失的通知,例如,當某個記錄被保存或者某個問題發生的時候。ABP定義了標準的API實現了該功能。
abp.notify.success('a message text', 'optional title'); abp.notify.info('a message text', 'optional title'); abp.notify.warn('a message text', 'optional title'); abp.notify.error('a message text', 'optional title');
做爲通知庫的 自定義選項,它也可以取得第3個參數(對象)。
通知API默認是使用toastr庫實現的。要使toastr生效,你應該引用toastr的css和javascript文件,而後再在頁面中包含abp.toastr.js做爲適配器。一個toastr成功通知以下所示:
你也能夠用你最喜歡的通知庫實現通知。只須要在自定義javascript文件中重寫全部的函數,而後把它添加到頁面中而不是abp.toastr.js中(你能夠檢查該文件看它是否實現,這個至關簡單)。
消息API被用來向用戶顯示一個消息或者從用戶那裏獲得一個確認。
消息API默認實現方式是使用了sweetalert庫。使用時你須要引用sweetalert的樣式和js,而後把 abp.sweet-alert.js 做爲適配器包含到你的頁面中。
以下所示:
abp.message.info('some info message', 'some optional title'); abp.message.success('some success message', 'some optional title'); abp.message.warn('some warning message', 'some optional title'); abp.message.error('some error message', 'some optional title');
成功的消息框顯示以下:
以下所示:
abp.message.confirm(
'User admin will be deleted.', //確認提示 'Are you sure?',//確認提示(可選參數) function (isConfirmed) { if (isConfirmed) { //...delete user 點擊確認後執行 } } );
第二個參數(標題)是可選的(因此,回調函數能夠做爲第二個參數)。
確認消息框顯示以下:
ABP在內部使用了消息API,例如:若是某個AJAX調用失敗,那麼它會調用abp.message.error。
ABP提供了有用的API,使整個頁面或者頁面的某個部分被遮罩層覆蓋實現阻塞或者繁忙指示(使用加載圖標表示繁忙)。
這個API使用一個透明的遮罩層(透明度可調節)來遮住整個頁面或者該頁面的某個元素。所以用戶不可以點擊。當你保存表單或者加載某個區域時(某個層或者整個頁面),這是至關有用的。
以下所示:
abp.ui.block(); //遮住整個頁面 abp.ui.block($('#MyDivElement')); //遮罩某個元素,在這裏可使用jQuery選擇器選擇元素.. abp.ui.block('#MyDivElement'); //..或者直接指定元素 abp.ui.unblock(); //解除遮罩 abp.ui.unblock('#MyDivElement'); //對指定元素解除遮罩
UI Block API 默認是使用jQuery插件block UI來實現的。爲了能正常運行,你須要引用腳本文件,而後包含 abp.blockUI.js 文件做爲適配器到你的頁面中。
該API被用來指示某些頁面或者元素正在忙碌(加載)。例如:當你提交表單數據到服務器的時候,你可能想要遮罩這個表單並顯示一個忙碌的指示器。
以下所示:
abp.ui.setBusy('#MyLoginForm'); abp.ui.clearBusy('#MyLoginForm');
參數應該是一個jQuery選擇器(如:#MyLoginForm)或者jQuery對象(如:$('#MyLoginForm'))。爲了使整個頁面都是在繁忙狀態,你應該傳遞null或者body做爲選擇器。
setBusy函數可以傳入一個promise(做爲第二個參數)而且自動的清除busy,當該promise完成的時候。以下所示:
abp.ui.setBusy(
$('#MyLoginForm'), abp.ajax({ ... }) );
因爲abp.ajax返回的是promise,因此咱們能直接使用它做爲參數。若是你想了解更多關於promise的資料,請查閱jQuery的Deferred。setBusy對Q提供支持(以及angulars的$http服務)。
UI Busy API 使用spin.js實現的。爲使其正常運行,你應該引用該腳本文件,而後在你的頁面中包含 abp.spin.js 做爲適配器。
Pub/Sub 事件模型被普遍的應用在客戶端。ABP包含了一個 簡單的全局事件總線 用來註冊事件而且觸發事件。
你可使用 abp.event.on 來 註冊 一個 全局事件 。示例以下:
abp.event.on('itemAddedToBasket', function (item) { console.log(item.name + ' is added to basket!'); });
第一個參數是 該事件的惟一名稱。另外一個參數是 回調函數,當指定的事件被觸發後將調用該參數。
你可使用 abp.event.off 方法來 卸載 已註冊的事件。
注意:爲了可以卸載指定的事件,應該提供相同的事件函數。
正如上面的示例所展現的,你應該將回調函數設置爲一個變量,而後在 on和off 中使用它。
abp.event.trigger 被用來觸發全局事件。觸發一個已註冊的事件的代碼以下:
abp.event.trigger('itemAddedToBasket', { id: 42, name: 'Acme Light MousePad' });
第一個參數是 該事件的惟一名稱。第二個是(可選的)事件參數。你能夠添加任何數量的參數,而且在回調方法中得到它們。
當你想要在客戶端寫一些簡單的日誌的時候,你可使用 console.log('...') API。可是,它不是全部的瀏覽器都支持該API,而且該函數也可能破壞你的腳本。因此,在使用的時候你首先應該檢查console是否有效。還有,你可能想在其它地方寫日誌。甚至你可能對寫日誌的等級也有要求。ABP定義了安全的日誌函數:
abp.log.debug('...'); abp.log.info('...'); abp.log.warn('...'); abp.log.error('...'); abp.log.fatal('...');
你能夠經過設置 abp.log.level 對 abp.log.levels 中的某個日誌等級進行更改(例如:abp.log.levels.INFO 不會記錄調試日誌)。這些函數默認將日誌記錄到了瀏覽器的控制檯裏了。但若是你須要的話,你也能夠重寫或者擴展這個行爲。
ABP提供了一些通用的工具函數。
用於建立更深的命名空間。假設咱們有一個基命名空間 abp,而後想要建立或者得到 abp.utils.strings.formatting 命名空間。不須要像下面這樣寫:
//建立或得到namespace abp.utils = abp.utils || {}; abp.utils.strings = abp.utils.strings || {}; abp.utils.strings.formatting = abp.utils.strings.formatting || {}; //給該namespace添加一個function abp.utils.strings.formatting.format = function() { ... };
咱們能夠這樣寫:
var formatting = abp.utils.createNamespace(abp, 'utils.strings.formatting'); //給該namespace添加一個function formatting.format = function() { ... };
這樣即安全又簡單的建立了更深層次的命名空間。注意,第一個參數是必須存在的根命名空間。
近似於C#中的string.Format()方法。示例以下:
var str = abp.utils.formatString('Hello {0}!', 'World'); //str = 'Hello World!' var str = abp.utils.formatString('{0} number is {1}.', 'Secret', 42); //str = 'Secret number is 42'