ABP官方文档翻译 8.1 通知系统
通知系统
介绍
在系统中通知用来基于特定的事件告知用户。ABP提供了pub/sub基础实时通知基础设施。
发送模型
有两种给用户发送通知的方式:
- 用户订阅一个特定的通知类型。然后我们发布此类型的一个通知,此类型会被发送给所有订阅的用户。这就是pub/sub模型。
- 我们可以直接给目标用户发送一个通知。
通知类型
通知有两种类型:
- 普通通知可以是任意类型。“如果一个用户发送给我一个友好的请求,通知我”就是这种通知类型的一个例子。
- 实体通知关联到一个特定的实体。“如果一个用户评论了这张照片,通知我”就是一种实体基础类型,因为它和一个特定的photo实体关联。用户可能想得到一些照片的通知,而不是全部。
通知数据
一个通知一般包含通知数据。例如:“如果一个用户发送给我一个友好的请求,通知我”通知会有两个数据属性:sender user name(哪个用户发送了这个友好请求)和request note(发送用户在请求中的笔记)。很显然通知数据的类型和通知类型紧密相连。不同的通知类型有不同的数据类型。
通知数据是可选的。一些通知可能不请求数据。这有预定义的通知数据类型,这些可以满足大多数场景。MessageNotificationData可以用于简单的信息,LocalizableMessageNotificationData可以用于本地化和参数化的通知信息。在之后的部分我们将会看到示例用法。
通知严重性
通知严重性有5种等级,定义在NotificationSeverity枚举中:Info,Success,Warn,Error和Fatal。默认值是Info。
关于通知持久化
参见通知存储部分了解更多关于通知持久化的信息。
订阅通知
INotificationSubscriptionManaer提供了API类来订阅通知。示例:
public class MyService : ITransientDependency
{
private readonly INotificationSubscriptionManager _notificationSubscriptionManager; public MyService(INotificationSubscriptionManager notificationSubscriptionManager)
{
_notificationSubscriptionManager = notificationSubscriptionManager;
} //Subscribe to a general notification
public async Task Subscribe_SentFrendshipRequest(int? tenantId, long userId)
{
await _notificationSubscriptionManager.SubscribeAsync(new UserIdentifier(tenantId, userId), "SentFrendshipRequest");
} //Subscribe to an entity notification
public async Task Subscribe_CommentPhoto(int? tenantId, long userId, Guid photoId)
{
await _notificationSubscriptionManager.SubscribeAsync(new UserIdentifier(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;
} //Send a general notification to a specific user
public async Task Publish_SentFrendshipRequest(string senderUserName, string friendshipMessage, UserIdentifier targetUserId)
{
await _notiticationPublisher.PublishAsync("SentFrendshipRequest", new SentFrendshipRequestNotificationData(senderUserName, friendshipMessage), userIds: new[] { targetUserId });
} //Send an entity notification to a specific user
public async Task Publish_CommentPhoto(string commenterUserName, string comment, Guid photoId, UserIdentifier photoOwnerUserId)
{
await _notiticationPublisher.PublishAsync("CommentPhoto", new CommentPhotoNotificationData(commenterUserName, comment), new EntityIdentifier(typeof(Photo), photoId), userIds: new[] { photoOwnerUserId });
} //Send a general notification to all subscribed users in current tenant (tenant in the session)
public async Task Publish_LowDisk(int remainingDiskInMb)
{
//Example "LowDiskWarningMessage" content for English -> "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);
}
}
在第一个例子中,我们发布了一个通知给一个单独的用户。SetFrendshipRequestNotificationData需要继承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;
}
}
在第二个示例中,我们发送一个特定实体的通知给一个特定的用户。通知数据类不需要序列化(因为默认会使用JSON序列化)。但是建议将它标记为序列化,因为在将来可能会在应用之间移动通知并且可能会使用二进制序列化。如之前所述,通知数据是可选的,并不是所有的通知都需要。
注意:如果我们给特定的用户发布了一个通知,他们不需要订阅这些通知。
在第三个示例中,我们没有定义一个专用的通知数据类。而是直接使用了內建的LocalizableMessageNotificationData并发布通知为'Warn'级别。LocalizableMessageNotificationData可以存储任意基于字典类型的数据(这也同样适用于自定义通知数据类,因为他们也继承自NotificationData类)。我们使用“remainingDiskInMb”作为本地化参数。本地化信息包含三个参数(如“Attention!Only{remainingDiskInMb}MBs left on the disk!”)。我们将在客户端部分看到如何本地化它。
用户通知管理
IUserNotificationManager用来管理用户通知。它有方法可以用来get,update,delete用户通知。你可以用它来准备一个通知列表页。
实时通知
当你使用IUserNotificationManager来查询通知时,我们通常希望推送实时通知到客户端。
通知系统使用IRealTimeNotifier来发送实时通知给用户。这可以被任何实时通信系统实现。在一个单独的包里,使用SignalR对它进行了实现。启动模板已经安装了SignalR。参见SignalR集成文档了解更多信息。
注意:通知系统在后台异步调用IRealTimeNotifier。所以,通知可能会有一些小延迟。
客户端
当一个事实通知被接收时,ABP在客户端触发一个全局事件。你可以按如下方式注册来获取通知:
abp.event.on('abp.notifications.received', function (userNotification) {
console.log(userNotification);
});
abp.notifications.received事件在每次接收到实时通知时触发。你可以按如上所示注册这个事件来获取通知。参见javascript event bus文档来获取关于事件的更多信息。一个通知"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:Unread,1:Read。
- 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.notifyapi来显示UI通知。
如果你需要实现这样的一个逻辑,这有一个简单且可伸缩的方式。当收到一个推送通知时,你可以仅使用一行代码就可以显示UI通知:
p.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内部所使用。
启动模板包含当收到一个推送通知时,显示UI通知的代码。
通知存储
通知系统使用INotificationStore来持久化通知。为了使通知系统正常工作,需要实现这个接口。你可以自己实现它或使用已经实现了它的module-zero.
通知定义
在使用通知之前,你可以不用定义它。不定义的话,你仅仅能使用任何通知名称。但是,定义通知会带来一些额外的好处。例如,你可以调查你应用中的所有通知。在这种情况下,我们可以为我们的模块定义一个通知提供者,如下:
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来获取通知定义。然后你可能想准备一个自动页来允许用户订阅这些通知。
ABP官方文档翻译 8.1 通知系统的更多相关文章
- ABP官方文档翻译 1.3 模块系统
ABP模块系统 介绍 模块定义 生命周期方法 PreInitialize Initialize PostInitialize Shutdown 模块依赖 插件系统 ASP.NET Core ASP.N ...
- ABP官方文档翻译 8.2 SignalR集成
SignalR集成 介绍 安装 服务器端 客户端 建立连接 內建特征 通知 在线客户端 PascalCase与CamelCase对比 你的SignalR代码 介绍 ABP中的Abp.Web.Signa ...
- ABP官方文档翻译 0.0 ABP官方文档翻译目录
一直想学习ABP,但囿于工作比较忙,没有合适的契机,当然最重要的还是自己懒.不知不觉从毕业到参加工作七年了,没留下点儿什么,总感觉很遗憾,所以今天终于卯足劲鼓起勇气开始写博客.有些事能做的很好,但要跟 ...
- ABP官方文档翻译 6.3 本地化
本地化 介绍 应用程序语言 本地化源 XML文件 注册XML本地化源 JSON文件 注册JSON本地化源 资源文件 自定义源 当前语言是如何决定的 ASP.NET Core ASP.NET MVC 5 ...
- ABP官方文档翻译 4.1 应用服务
应用服务 IApplicationService接口 ApplicationService类 CrudService和AsyncCrudAppService类 简单的CRUD应用服务示例 自定义CRU ...
- ABP官方文档翻译 7.1 后台Jobs和Workers
后台Jobs和Workers 介绍 后台Jobs 关于Job持久化 创建后台Job 在队列中添加一个新Job 默认的后台Job管理器 后台Job存储 配置 禁用Job执行 异常处理 Hangfire集 ...
- ABP官方文档翻译 3.7 领域事件(事件总线)
领域事件(事件总线) 事件总线 注入IEventBus 获取默认实例 定义事件 预定义事件 处理异常 实体更改 触发事件 处理事件 处理基础事件 处理者异常 处理多个事件 注册处理者 自动 手动 取消 ...
- ABP官方文档翻译 2.1 依赖注入
依赖注入 什么是依赖注入 传统方式的问题 解决方案 构造函数注入模式 属性注入模式 依赖注入框架 ABP依赖注入基础设施 注册依赖注入 传统注册 帮助接口 自定义/直接注册 使用IocManager ...
- ABP官方文档翻译 1.2 N层架构
N层架构 介绍 ABP架构 其他(通用) 领域层 应用层 基础设施层 网络和展现层 其他 总结 介绍 应用程序代码库的分层架构是被广泛认可的可以减少程序复杂度.提高代码复用率的技术.为了实现分层架构, ...
随机推荐
- qscoj 128 喵哈哈村的魔法源泉(2)(模仿快速幂,好题)
喵哈哈村的魔法源泉(2) 发布时间: 2017年5月9日 20:59 最后更新: 2017年5月9日 21:00 时间限制: 1000ms 内存限制: 128M 描述 喵哈哈村有一个魔法源 ...
- 51Nod 1277 字符串中的最大值(KMP,裸题)
1277 字符串中的最大值 题目来源: Codility 基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题 一个字符串的前缀是指包含该字符第一个字母的连续子串,例如: ...
- BZOJ 1018: [SHOI2008]堵塞的交通traffic(线段树)
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1018 用线段树维护区间连通性,对于每一个区间记录6个域表示(左上,左下)(左上,右上)(右上, ...
- redis3.0 集群在windows上的配置(转)
1. 安装Redis版本:win-3.0.501https://github.com/MSOpenTech/redis/releases页面有,我下载的是zip版本的:Redis-x64-3.0.50 ...
- 算法-java代码实现归并排序
归并排序 对于一个int数组,请编写一个归并排序算法,对数组元素排序. 给定一个int数组A及数组的大小n,请返回排序后的数组. 测试样例: [1,2,3,5,2,3],6 [1,2,2,3,3,5] ...
- API接口安全性设计
http://www.jianshu.com/p/c6518a8f4040 接口的安全性主要围绕Token.Timestamp和Sign三个机制展开设计,保证接口的数据不会被篡改和重复调用,下面具体来 ...
- 解决方案 git@github.com出现Permission denied (publickey)
ubentu 13.10 git version 1.8.3.2 解决方案:ssh -T git@github.com出现Permission denied (publickey).的问题 今天的任 ...
- 解决:mysql is blocked because of many connection errors;
标签:because service foreign errors closed 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http:// ...
- 5.04 toArray()有一个问题须要解决一下
把查询数据转为数组输出,这个toArray()方法是把对像转为数组输出,本身是没啥 问题.但是里面好像少写了一句判断:应先判断这个对像是否为空!如果为空则不转换直接输出空就行了吗,否则一个空值去转成数 ...
- struts异常:Caused by: Parent package is not defined: json-default - [unknown location]解决办法
问题描述: Unable to load configuration. - [unknown location] at com.opensymphony.xwork2.config.Configura ...