本篇目錄
介紹
通知(Notification)用於告知用戶系統中的特定事件。ABP提供了基於實時通知基礎設施的發布訂閱模型(pub/sub)。
發送模型
給用戶發送通知有兩種方式:
- 首先用戶訂閱特定的通知類型,然后我們發布這種類型的通知,這種類型的通知會傳遞給所有已經訂閱的用戶。這就是發布訂閱(pub/sub)模型。
- 直接給目標用戶發送通知。
通知類型
通知類型也有兩種:
- 一般通知:是任意類型的通知。“如果一個用戶給我發送了添加好友的請求就通知我”是這種通知類型的一個例子。
- 實體通知:和特定的實體相關聯。“如果一個用戶在 這張圖片上評論那么通知我”是基於實體的通知,因為它和特定的實體相關聯。用戶可能想獲得某些圖片的通知而不是所有的圖片通知。
通知數據
一個通知一般都會包括一個通知數據。比如,“如果一個用戶給我發送了添加好友的請求就通知我”,這個通知可能有兩個數據屬性:發送者用戶名(那哪一個用戶發送的這個添加好友的請求)和請求內容(該用戶在這個請求中寫的東西)。很明顯,該通知數據類型緊耦合於通知類型。不同的通知類型有不同的數據類型。
- 通知數據是可選的。某些通知可能不需要數據。一些預定義的通知數據類型可能對於大多數情況夠用了。 MessageNotificationData可以用於簡單的信息, LocalizableMessageNotificationData可以用於本地化的,帶參數的通知信息。
通知安全
通知的安全性有5個級別,它們定義在NotificationSeverity枚舉類中,分別是 Info,Success,Warn,Error和Fatal。默認值是Info。
訂閱通知
INotificationSubscriptionManager 提供了訂閱通知的API。例如:
public class MyService : ITransientDependency
{
private readonly INotificationSubscriptionManager _notificationSubscriptionManager;
public MyService(INotificationSubscriptionManager notificationSubscriptionManager)
{
_notificationSubscriptionManager = notificationSubscriptionManager;
}
//訂閱一個一般通知
public async Task Subscribe_SentFrendshipRequest(int? tenantId, long userId)
{
await _notificationSubscriptionManager.SubscribeAsync(tenantId, userId, "SentFrendshipRequest");
}
//訂閱一個實體通知
public async Task Subscribe_CommentPhoto(int? tenantId, long userId, Guid photoId)
{
await _notificationSubscriptionManager.SubscribeAsync(tenantId, userId, "CommentPhoto", new EntityIdentifier(typeof(Photo), photoId));
}
}
首先,我們注入了INotificationSubscriptionManager。第一個方法訂閱了一個一般通知。當某人發送了一個添加好友的請求時,用戶想得到通知。第二個方法訂閱了一個和特定實體(Photo)相關的通知。如果任何人對這個特定的圖片進行了評論,那么用戶就會收到通知。
每一個通知應該有唯一的名字(比如例子中的SentFrendshipRequest和 CommentPhoto)。
INotificationSubscriptionManager還有很多方法來管理通知,比如UnsubscribeAsync, IsSubscribedAsync, GetSubscriptionsAsync等方法。
發布通知
INotificationPublisher用於發布通知,例如:
public class MyService : ITransientDependency
{
private readonly INotificationPublisher _notiticationPublisher;
public MyService(INotificationPublisher notiticationPublisher)
{
_notiticationPublisher = notiticationPublisher;
}
//給特定的用戶發送一個一般通知
public async Task Publish_SentFrendshipRequest(string senderUserName, string friendshipMessage, long targetUserId)
{
await _notiticationPublisher.PublishAsync("SentFrendshipRequest", new SentFrendshipRequestNotificationData(senderUserName, friendshipMessage), userIds: new[] { targetUserId });
}
//給特定的用戶發送一個實體通知
public async Task Publish_CommentPhoto(string commenterUserName, string comment, Guid photoId, long photoOwnerUserId)
{
await _notiticationPublisher.PublishAsync("CommentPhoto", new CommentPhotoNotificationData(commenterUserName, comment), new EntityIdentifier(typeof(Photo), photoId), userIds: new[] { photoOwnerUserId });
}
//給具特定嚴重級別程度的所有訂閱用戶發送一個一般通知
public async Task Publish_LowDisk(int remainingDiskInMb)
{
//例如 "LowDiskWarningMessage"的英文內容 -> "Attention! Only {remainingDiskInMb} MBs left on the disk!"
var data = new LocalizableMessageNotificationData(new LocalizableString("LowDiskWarningMessage", "MyLocalizationSourceName"));
data["remainingDiskInMb"] = remainingDiskInMb;
await _notiticationPublisher.PublishAsync("System.LowDisk", data, severity: NotificationSeverity.Warn);
}
}
在第一個例子中,我們向單個用戶發布了一個通知。SentFrendshipRequestNotificationData應該從 NotificationData繼承,比如:
[Serializable]
public class SentFrendshipRequestNotificationData : NotificationData
{
public string SenderUserName { get; set; }
public string FriendshipMessage { get; set; }
public SentFrendshipRequestNotificationData(string senderUserName, string friendshipMessage)
{
SenderUserName = senderUserName;
FriendshipMessage = friendshipMessage;
}
}
在第二個例子中,我們向特定的用戶發送了一個特定實體的通知。通知數據類CommentPhotoNotificationData一般不需要serializble(因為默認使用了JSON序列化)。但是建議把它標記為serializable,因為你可能需要在應用之間移動通知,也可能在未來使用二進制的序列。此外,正如之前聲明的那樣,通知數據是可選的,而且對於所有的通知可能不是必須的。
注意:如果我們對特定的用戶發布了一個通知,那么他們不需要訂閱那些通知。
在第三個例子中,我們沒有定義一個專用的通知數據類。相反,我們直接使用了內置的具有基於字典數據的LocalizableMessageNotificationData,並且以 Warn發布了通知。LocalizableMessageNotificationData可以存儲基於字典的任意數據(這對於自定義的通知數據類也是成立的,因為它們也從 NotificationData類繼承)。在本地化時我們使用了“ remaingDiskInMb”作為參數。本地化信息可以包含這些參數(比如例子中的"Attention! Only {remainingDiskInMb} MBs left on the disk!")
用戶通知管理者
IUserNotificationManager用於管理用戶的通知,它有 get,update或delete用戶通知的方法。你可以為你的應用程序准備一個通知列表頁面。
實時通知
當使用IUserNotificationManager來查詢通知時,我們一般想實時地將通知推送到客戶端。
通知系統使用了IRealTimeNotifier向用戶發送實時通知。這可以使用任何類型的實時通訊系統來實現。ABP在一個單獨的包中使用了 SignalR來實現。ABP官網的起始模板已經安裝了SignalR。
注意:通知系統在一個后台工作中異步調用IRealTimeNotifier。因此,通知發送時可能伴有輕微的延遲。
客戶端
當接收到一個實時通知時,ABP會在客戶端觸發一個全局的事件(global event)。你可以像下面那樣注冊來獲取通知:
abp.event.on('abp.notifications.received', function (userNotification) {
console.log(userNotification);
});
abp.notifications.received事件對於每個接收到的實時通知都會觸發。你可以像上面那樣注冊該事件來獲取通知。查看javascript事件總線文檔獲取更多信息。對於上面的“System.LowDisk”例子,下面是接收到的通知消息的JSON數據:
{
"userId": 2,
"state": 0,
"notification": {
"notificationName": "System.LowDisk",
"data": {
"message": {
"sourceName": "MyLocalizationSourceName",
"name": "LowDiskWarningMessage"
},
"type": "Abp.Notifications.LocalizableMessageNotificationData",
"properties": {
"remainingDiskInMb": "42"
}
},
"entityType": null,
"entityTypeName": null,
"entityId": null,
"severity": 0,
"creationTime": "2016-02-09T17:03:32.13",
"id": "0263d581-3d8a-476b-8e16-4f6a6f10a632"
},
"id": "4a546baf-bf17-4924-b993-32e420a8d468"
}
在這個對象中,
- userId:當前的用戶Id。一般來說不需要這個,因為你知道當前的用戶。
- state: UserNotificationState枚舉的值。0:未讀,1:已讀。
- notification:通知細節。
- notificationName:通知的唯一名稱。(當發布該通知時使用相同的值)
- data:通知數據。在本例中,我們使用了**LocalizableMessageNotificationData **(正如之前的例子中發布的)。
- message:本地的信息通知。我們可以使用 sourceName和 name來本地化UI上的信息。
- type:通知數據類型。全類型名稱,包括命名空間。當處理該通知數據時我們可以檢查該類型。
- properties:基於字典的自定義屬性。
- entityType, entityTypeName和entityId:和通知相關的實體的信息。
- severity: NotificationSeverity枚舉的值。0: Info, 1: Success, 2: Warn, 3: Error, 4: Fatal。
- creationTime:該通知創建的時間。
- id:通知的Id。
- id:用戶的通知id。
當然,你不僅可以記錄該通知。你還可以使用通知數據將通知信息展示給該用戶。例如:
abp.event.on('abp.notifications.received', function (userNotification) {
if (userNotification.notification.data.type === 'Abp.Notifications.LocalizableMessageNotificationData') {
var localizedText = abp.localization.localize(
userNotification.notification.data.message.name,
userNotification.notification.data.message.sourceName
);
$.each(userNotification.notification.data.properties, function (key, value) {
localizedText = localizedText.replace('{' + key + '}', value);
});
alert('New localized notification: ' + localizedText);
} else if (userNotification.notification.data.type === 'Abp.Notifications.MessageNotificationData') {
alert('New simple notification: ' + userNotification.notification.data.message);
}
});
要處理通知數據,我們應該檢查該數據類型。這個例子簡單地從通知數據中獲得信息。對於本地化的信息(LocalizableMessageNotificationData),我們要本地化該信息並替換參數。對於簡單的信息(MessageNotificationData),我們直接獲得該信息。當然,在一個真實項目中,我們不使用alert函數。我們可以使用abp.notify
api來展示漂亮的UI通知。
如果你要實現這么上面的邏輯,那么有一種更簡單且可伸縮的方式。當接收到一個push通知時,你可以只使用一句代碼來顯示UI通知:
abp.event.on('abp.notifications.received', function (userNotification) {
abp.notifications.showUiNotifyForUserNotification(userNotification);
});
這句代碼會展示一個UI通知如下所示(上面System.LowDisk通知的例子):
對於內置的通知數據類型(LocalizableMessageNotificationData和 MessageNotificationData)是有效的。如果你有自定義的通知數據類型,那么你應該注冊數據格式化器,如下所示:
abp.notifications.messageFormatters['MyProject.MyNotificationDataType'] = function(userNotification) {
return ...; //format and return message here
};
這樣,showUiNotifyForUserNotification就可以對你的數據類型創建要顯示的信息了。如果你只需要格式化信息,那么你可以直接使用 abp.notifications.getFormattedMessageFromUserNotification(userNotification),它內部還是使用了showUiNotifyForUserNotification。
當接收到一個push通知時,啟動模板包含了展示UI通知的代碼。
通知存儲
通知系統使用了INotificationStore來持久化通知。為了使通知系統合適地工作,應該實現這個接口。你可以自己實現或者使用module-zero(它已經實現了該接口)。
通知定義
在使用之前你不必定義一個通知。你無需定義任何類型的通知名就可以使用它們。但是,定義通知名可能會給你帶來額外的好處。比如,你以后要研究應用程序中所有的通知,在這種情況下,我們可以給我們的模塊定義一個通知提供器(notification provider ),如下所示:
public class MyAppNotificationProvider : NotificationProvider
{
public override void SetNotifications(INotificationDefinitionContext context)
{
context.Manager.Add(
new NotificationDefinition(
"App.NewUserRegistered",
displayName: new LocalizableString("NewUserRegisteredNotificationDefinition", "MyLocalizationSourceName"),
permissionDependency: new SimplePermissionDependency("App.Pages.UserManagement")
)
);
}
}
"App.NewUserRegistered"是該通知的唯一名稱。我們定義了一個本地的 displayName(然后當在UI上訂閱了該通知時,我們可以顯示該通知)。最后,我們聲明了只有擁有了"App.Pages.UserManagement"權限的用戶才可以使用該通知。
也有一些其他的參數,你可以在代碼中研究。對於一個通知定義,只有通知名稱是必須的。
當定義了這么一個通知提供器之后,我們應該在模塊的PreInitialize事件中進行注冊,如下所示:
public class AbpZeroTemplateCoreModule : AbpModule
{
public override void PreInitialize()
{
Configuration.Notifications.Providers.Add<MyAppNotificationProvider>();
}
//...
}
最后,要獲得通知定義,在應用程序中注入並使用INotificationDefinitionManager。