ABP+AdminLTE+Bootstrap Table權限管理系統第七節--登錄邏輯及幾種abp封裝的Javascript函數庫


返回總目錄:ABP+AdminLTE+Bootstrap Table權限管理系統一期

        簡介

       經過前幾節,我們已經解決數據庫,模型,DTO,控制器和注入等問題.那么再來看一下登錄邏輯.這里算是前面幾節的一個初次試水.

首先我們數據庫已經有的相應的數據.

添加Login方法     

  模型和DTO已經建好,所以我們直接在服務層添加Login方法就可以了.

注入IUserService接口

       

在展現層添加Account控制器,注入IUserService接口,調用Login方法.

  

添加視圖頁面.

  然后添加視圖頁面.

 效果

    運行一下,看一下結果.

   注意事項

除了頁面比較漂亮(哈哈),這些本來都沒有什么好說的,直接上圖,

這里值得注意的是,我們在創建下面的方法,在調用接口的的時候會報一個錯誤:web的App_Data/Logs/Logs.txt日志文件中查看到打印到錯誤信息。

     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封裝的Javascript函數庫

消息

用於向用戶顯示對話框,展示消息或者得到用戶的確認,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');

Js日志接口

這個主要是對瀏覽器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!');
    };

格式化字符串(abp.utils.formatString)

    和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通常會有以下步驟:

  • 基本上:為了執行一個AJAX調用,首先你要在客戶端提供一個可供請求的URL,選取提交數據和一個方法(GET,POST,PUT,DELETE)。

  • 等待調用完成后,處理返回結果。當執行AJAX調用服務器端的時候,可能會有錯誤(一般是網絡錯誤)。當然也有可能是服務器端產生了一些錯誤,對於這些錯誤會,服務器會返回一個失敗的響應並且附上錯誤消息給客戶端。

  • 客戶端代碼應該處理這些錯誤,並且可以選擇通知用戶(可以顯示一個錯誤對話框)。如果沒有錯誤且服務器端返回了數據,客戶端必須處理它。還有你應該限制頁面的某個區域(或者整個頁面),並顯示一個忙碌的指示直到AJAX操作完成。

  • 服務器端在得到請求后執行服務器端代碼,捕獲異常並返回一個有效的響應給客戶端。在錯誤情況下,可以選擇發送錯誤消息給客戶端。如果是驗證錯誤,服務器端可以添加驗證錯誤的驗證信息。在成功情況下,可以發送返回值給客戶端。

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在背后做了很多重要的處理。讓我們深入了解一下:

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函數中處理返回值。

處理錯誤

正如上面所述,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:所有其它類型的異常。

 WrapResult和DontWrapResult特性

使用 WrapResult和DontWrapResult 特性,可以對控制器的某個action或者所有的action來控制包裝。

ASP.NET MVC 控制器

如果返回的類型是 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()...)。

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()...)。

動態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能夠自動的生成這些函數。

詳情請閱讀動態Web API層文檔ASP.NET Core文檔

Javascript Notification API

當一些事情發生的時候,我們喜歡顯示一些別致的能夠自動消失的通知,例如,當某個記錄被保存或者某個問題發生的時候。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中(你可以檢查該文件看它是否實現,這個相當簡單)。

abp.message簡介

消息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'); 

成功的消息框顯示如下:

 

Confirmation對話框

如下所示:

abp.message.confirm(
    'User admin will be deleted.', //確認提示 'Are you sure?',//確認提示(可選參數) function (isConfirmed) { if (isConfirmed) { //...delete user 點擊確認后執行 } } ); 

第二個參數(標題)是可選的(所以,回調函數可以作為第二個參數)。

確認消息框顯示如下:

 

ABP在內部使用了消息API,例如:如果某個AJAX調用失敗,那么它會調用abp.message.error。

Javascript UI Block & Busy API

ABP提供了有用的API,使整個頁面或者頁面的某個部分被遮罩層覆蓋實現阻塞或者繁忙指示(使用加載圖標表示繁忙)。

UI Block 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 文件作為適配器到你的頁面中。

UI Busy API

該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 作為適配器。

ABP表現層 - 事件總線EventBUS

簡介

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' }); 

第一個參數是 該事件的唯一名稱。第二個是(可選的)事件參數。你可以添加任何數量的參數,並且在回調方法中獲得它們。

ABP表現層 - Javascript 日志 API

簡介

當你想要在客戶端寫一些簡單的日志的時候,你可以使用 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表現層 - 其他工具函數OtherUtilities

ABP提供了一些通用的工具函數。

abp.utils.createNamespace

用於創建更深的命名空間。假設我們有一個基命名空間 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() { ... }; 

這樣即安全又簡單的創建了更深層次的命名空間。注意,第一個參數是必須存在的根命名空間。

abp.utils.formatString

近似於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'

返回總目錄:ABP+AdminLTE+Bootstrap Table權限管理系統一期


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM