FLSY.Middleware
1.1.0
dotnet add package FLSY.Middleware --version 1.1.0
NuGet\Install-Package FLSY.Middleware -Version 1.1.0
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="FLSY.Middleware" Version="1.1.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add FLSY.Middleware --version 1.1.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
#r "nuget: FLSY.Middleware, 1.1.0"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install FLSY.Middleware as a Cake Addin #addin nuget:?package=FLSY.Middleware&version=1.1.0 // Install FLSY.Middleware as a Cake Tool #tool nuget:?package=FLSY.Middleware&version=1.1.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
一、ABP.Authentication身份认证
依赖包:已安装
Install-Package JWT -Version 10.0.2
配置:放入appsettings.json文件里
{
"Authentication": {
"SecretKey": "用户授权、鉴权的密钥",
"TokenEffectiveMinutes": Token有效分钟数,
"RefreshTokenMinutes": Token在过期多少分钟前刷新Token
}
}
初始化:
1. 引入ABP依赖模块:AbpAuthenticationModule
过滤器:
AuthenticationAttribute,用于对请求头部的Token进行校验
1. 鉴权
2. 有效期验证
3. 当Token即将过期刷新Token,刷新的Token会放到响应头部中,Key=NewToken
4. 将授权数据写入HttpContext.Items.Add("Auth_Data", "授权数据");
扩展方法:
1. 获取授权数据:string GetAuthenticationData(this HttpContext context)
使用说明:
1. 引入依赖注入实例IAuthenticationService
public interface IAuthenticationService
{
/// <summary>
/// 授权
/// </summary>
/// <param name="data">授权数据:用于存储跟授权用户相关的数据</param>
/// <returns>返回Token</returns>
string Authorization(Dictionary<string, object> data);
/// <summary>
/// 鉴权
/// </summary>
/// <param name="token">访问令牌</param>
/// <returns>授权用户相关的数据</returns>
string Authentication(string token);
/// <summary>
/// 是否刷新Token
/// </summary>
/// <param name="expTime">token过期时间</param>
/// <returns></returns>
bool IsRefreshToke(DateTime expTime);
}
二、ABP.DataEncryption数据加密
依赖包:已安装
Install-Package Portable.BouncyCastle -Version 1.9.0
初始化:
1. 引入ABP依赖模块:AbpDataEncryptionModule
2. 在配置服务容器中添加以下代码
public override void ConfigureServices(ServiceConfigurationContext context)
{
var services = context.Services;
services.AddDataEncryption(options =>
{
options.RSAPublic = "RSA加密公钥";
options.RSAPrivate = "RSA加密私钥";
});
}
前端:
1. RsaPublicKey=获取公钥
2. AesKey=随机生成一个字符串作为对称加密的密钥,16个字符
3. 用AesKey对请求数据进行对称加密,加密后的密文放到请求body里
4. 用公钥RsaPublicKey对密钥AesKey进行非对称加密,加密后的密文放到请求头部里,key=AesKey
5. 请求头部的数据类型=Aes,Content-Type=Aes
后端:
1. 在需要加密的实体类上面加上注解[ModelBinder(BinderType = typeof(DataEncryption.DataEncryptionModelBinder))]
2. 在接口参数左边加上参数注解[FromBody]
三、ABP.DataValidation数据验证
初始化:
1. 引入ABP依赖模块:AbpModelStateValidatorModule
基于数据注解验证:
Required:指定属性不能为空
Range:指定数值属性的范围
RegularExpression:指定属性的正则表达式
MaxLength:指定字符串属性的最大长度
MinLength:指定字符串属性的最小长度
DataType:指定属性的数据类型,例如日期、电子邮件或 URL 等
Compare:指定要比较的属性名称,例如两次输入密码是否一致等
四、ABP.Hangfire任务调度
依赖包:已安装
Install-Package Hangfire -Version 1.8.1
Install-Package Hangfire.Dashboard.BasicAuthorization -Version 1.0.2
Install-Package Hangfire.HttpJob -Version 3.7.6
Install-Package Hangfire.HttpJob.Client -Version 1.2.9
Install-Package Hangfire.Redis.StackExchange -Version 1.8.7
配置:放入appsettings.json文件里
{
"Hangfire": {
"ConnectionString": "数据库连接字符串",
"DbType": 2, //数据库类型,1=SqlServer|2=Redis,默认为SqlServer
"DefaultDatabase": 10, //如果使用的是Redis,可以指定默认使用的数据库,不指定为0
"UserName": "后台管理授权账号",
"Password": "后台管理授权密码"
}
}
初始化:
1. 引入ABP依赖模块:AbpHangfireModule
后台管理:路由地址/hangfire
1. 添加定时任务
2. 查看定时任务执行结果
五、ABP.LogDashboard日志记录
依赖包:已安装
Install-Package LogDashboard -Version 1.4.8
Install-Package Serilog.AspNetCore -Version 6.1.0
配置:放入appsettings.json文件里
{
"LogDashboard": {
"UserName": "后台管理授权账号",
"Password": "后台管理授权密码"
}
}
初始化:
1. 引入ABP依赖模块:AbpLogDashboardModule
过滤器:
ApiLogFilterAttribute 记录接口请求响应日志
ErrorLogFilterAttribute 记录系统异常日志
自定义属性:
NotRecordApiLog 不记录日志
NotRecordApiRequestLog 不记录接口请求日志
NotRecordApiResponseLog 不记录接口响应日志
后台管理:路由地址/logdashboard
1. 查看日志
六、ABP.RabbitMQ消息队列
依赖包:已安装
Install-Package RabbitMQ.Client -Version 6.5.0
配置:放入appsettings.json文件里
{
"RabbitMQ": {
"Host": "主机地址",
"Port": 端口号,
"UserName": "账号",
"PassWord": "密码",
"VirtualHost": "/"
}
}
初始化:
1. 引入ABP依赖模块:AbpRabbitMQModule
使用说明:
1. 引入依赖注入实例IRabbitMQService
/// <summary>
/// MQ消息队列服务
/// </summary>
public interface IRabbitMQService
{
/// <summary>
/// 发送消息
/// </summary>
/// <param name="queueName"></param>
/// <param name="msg"></param>
void Publish(string queueName, string msg);
/// <summary>
/// 发送消息
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="queueName"></param>
/// <param name="msg"></param>
void Publish<T>(string queueName, T msg);
/// <summary>
/// 消费消息
/// </summary>
/// <param name="queueName"></param>
/// <param name="consumeFunc"></param>
/// <returns></returns>
string Consume(string queueName, Func<string, bool> consumeFunc);
/// <summary>
/// 消费消息
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="queueName"></param>
/// <param name="consumeFunc"></param>
/// <returns></returns>
string Consume<T>(string queueName, Func<T, bool> consumeFunc);
}
七、ABP.Redis缓存
依赖包:已安装
Install-Package StackExchange.Redis -Version 2.6.104
配置:放入appsettings.json文件里
{
"Redis": {
"ConnectionString": "Redis数据库连接字符串",
"DefaultDatabase": 0,//默认使用的数据库,不指定为0
"Enable": true,//是否启用,如果不启用会用内存缓存
"IsEnableDistributedLocks": false,//是否启用分布式锁
"LockExpiry": 3//锁过期时间:单位秒
}
}
初始化:
1. 引入ABP依赖模块:AbpRedisModule
使用说明:
1. 引入依赖注入实例ICacheService
/// <summary>
/// 缓存服务
/// </summary>
public interface ICacheService
{
/// <summary>
/// 添加对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="expiry">过期时间</param>
/// <param name="dbIndex">数据库索引,如果为null则使用配置文件里的DefaultDatabase</param>
/// <returns></returns>
bool Add<T>(string key, T value, TimeSpan? expiry = null, int? dbIndex = null);
/// <summary>
/// 添加字符串
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="expiry"></param>
/// <param name="dbIndex"></param>
/// <returns></returns>
bool AddString(string key, string value, TimeSpan? expiry = null, int? dbIndex = null);
/// <summary>
/// 获取对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="key"></param>
/// <param name="dbIndex"></param>
/// <returns></returns>
T Get<T>(string key, int? dbIndex = null);
/// <summary>
/// 获取字符串
/// </summary>
/// <param name="key"></param>
/// <param name="dbIndex"></param>
/// <returns></returns>
string GetString(string key, int? dbIndex = null);
/// <summary>
/// 移除缓存
/// </summary>
/// <param name="key"></param>
/// <param name="dbIndex"></param>
/// <returns></returns>
bool Remove(string key, int? dbIndex = null);
/// <summary>
/// 搜索key
/// </summary>
/// <param name="pattern">搜索关键字</param>
/// <param name="dbIndex"></param>
/// <returns></returns>
IEnumerable<string> GetAllKey(string pattern, int? dbIndex = null);
/// <summary>
/// 缓存是否存在
/// </summary>
/// <param name="key"></param>
/// <param name="dbIndex"></param>
/// <returns></returns>
bool Exists(string key, int? dbIndex = null);
/// <summary>
/// 设置缓存过期时间
/// </summary>
/// <param name="key"></param>
/// <param name="expiry"></param>
/// <param name="dbIndex"></param>
/// <returns></returns>
bool KeyExpire(string key, TimeSpan? expiry, int? dbIndex = null);
/// <summary>
/// 生成自增ID
/// </summary>
/// <param name="key"></param>
/// <param name="dbIndex"></param>
/// <returns></returns>
Task<long> GenerateIdAsync(string key, int? dbIndex = null);
}
八、ABP.SqlSugar数据库访问
依赖包:已安装
Install-Package SqlSugarCore -Version 5.1.4.69
配置:放入appsettings.json文件里
{
"SqlSugar": {
"ConnConfigList": [
{
"Tenant": "租户名称",
"ConnStr": "数据库连接字符串",
"DbType": "数据库类型:0=MySql,1=SqlServer,2=Sqlite,默认值为1",
"IsAutoCloseConnection": "是否自动关闭连接"
}
],//数据库连接配置集合
"CommandTimeOut": "执行命令超时时间:默认30秒",
"IsWithNoLockQuery": "是否脏读:默认值true",
"IsUtcNow": "是否是Utc时间:默认值false",
"WorkId":"用于生成雪花ID的:不同的机器不要配置一样的,默认值1"
}
}
初始化:
1. 引入ABP依赖模块:AbpSqlSugarModule
使用说明:
1.引入依赖注入实例:ISqlSugarService
/// <summary>
/// 数据库访问服务
/// </summary>
public interface ISqlSugarService
{
/// <summary>
/// 数据库访问客户端
/// </summary>
SqlSugarClient Client { get; }
/// <summary>
/// 获取雪花ID
/// </summary>
/// <returns></returns>
long GetSnowFlakeSingle();
}
九、ABP.Swagger接口文档
依赖包:已安装
Install-Package Swashbuckle.AspNetCore -Version 6.5.0
Install-Package Swashbuckle.AspNetCore.Annotations -Version 6.5.0
初始化:
1. 引入ABP依赖模块:AbpSwaggerModule
使用说明:
1. 在浏览器中输入路由地址:/swagger即可查看接口文档
十、ABP.Kafka消息队列
依赖包:已安装
Install-Package Confluent.Kafka -Version 1.9.0
Install-Package protobuf-net -Version 3.1.17
配置:放入appsettings.json文件里
{
"Kafka": {
"ProduceBootstrapServers": "生产者 kafka 代理主机:端口",
"ConsumeBootstrapServers": "消费者 kakfa 代理主机:端口",
"UserName":"认证账号",
"Password":"认证密码"
}
}
初始化:
1. 引入ABP依赖模块:AbpKafkaModule
使用说明:
1. 拥入依赖注入实例:ICommonConfulentKafka
public interface ICommonConfulentKafka
{
/// <summary>
/// 获取主题分区数
/// </summary>
/// <param name="topicName">主题</param>
/// <returns></returns>
int? GetPartitionsByTopic(string topicName);
/// <summary>
/// 删除标题
/// </summary>
/// <param name="topicName">标题名称</param>
/// <returns></returns>
Task DeleteTopicAsync(string topicName);
/// <summary>
/// 创建标题
/// </summary>
/// <param name="topicName">标题名称</param>
/// <returns></returns>
Task CreateTopicAsync(string topicName);
/// <summary>
/// 生产者 发送消息
/// </summary>
/// <typeparam name="T">数据类型</typeparam>
/// <param name="topicName">标题</param>
/// <param name="msg">消息内容</param>
/// <returns></returns>
Task ProduceMsgAsync<T>(string topicName, T msg);
/// <summary>
/// 消费者 接收消息
/// </summary>
/// <typeparam name="T">数据类型</typeparam>
/// <param name="topics">标题类型</param>
/// <param name="group">组名</param>
/// <returns></returns>
IConsumer<Ignore, T> ConsumeMsg<T>(string group);
/// <summary>
/// 生产者 发送Proto消息
/// </summary>
/// <typeparam name="T">数据类型</typeparam>
/// <param name="topicName">标题</param>
/// <param name="msg">消息内容</param>
/// <returns></returns>
Task ProduceProtoBufMsgAsync<T>(string topicName, T msg);
/// <summary>
/// 消费者 接收Proto消息
/// </summary>
/// <typeparam name="T">数据类型</typeparam>
/// <param name="topics">标题类型</param>
/// <param name="group">组名</param>
/// <returns></returns>
IConsumer<Ignore, T> ConsumeProtoBufMsg<T>(string group);
}
十一、ABP.Mail邮件服务
配置:放入appsettings.json文件里
{
"Mail": {
"Host": "邮件服务商主机地址",
"Port": "邮件服务商主机端口号",
"SenderEmail": "发件人邮箱",
"SenderPwd": "发件人密码",
"SenderName": "发件人名称",
"WriteBackEmail": "回信邮箱"
}
}
初始化:
1. 引入ABP依赖模块:AbpMailModule
使用说明:
1.引入依赖注入实例:IMailService
/// <summary>
/// 邮件服务
/// </summary>
public interface IMailService
{
/// <summary>
/// 发送邮件
/// </summary>
/// <param name="sendMailOptions"></param>
void Send(SendMailOptions sendMailOptions);
}
发送邮件参数说明SendMailOptions:
AddresseeEmail:收件人邮箱。
Subject:主题。
Contents:内容集合
ContentType:内容类型text/html|text/plain|text/richtext|text/xml
更多类型查看MediaTypeNames常量。
Content:内容文本。
ContentEncoding:内容编码。
Attachments:附件集合。
十二、ABP.SMS短信服务
依赖包:已安装
阿里云短信服务开发包
Install-Package AlibabaCloud.SDK.Dysmsapi20170525 -Version 2.0.23
配置:放入appsettings.json文件里
{
"SmsOptions": {
"AccessKeyId": "账号ID",
"AccessKeySecret": "api访问密钥"
}//短信平台参数
}
初始化:
1. 引入ABP依赖模块:AbpSmsModule
使用说明:
1.引入依赖注入实例:ISmsService
/// <summary>
/// 短信服务
/// </summary>
public interface ISmsService
{
/// <summary>
/// 发送短信
/// </summary>
/// <param name="sendSmsOptions"></param>
/// <returns></returns>
bool Send(SendSmsOptions sendSmsOptions);
}
发送短信参数说明SendSmsOptions:
PhoneNumbers:接收人手机号码。
SignName:签名,显示在短信前面,需要去短信平台申请。
TemplateCode:模板CODE,需要去短信平台申请。
TemplateParam:模板参数:json对象字符串。
十三、ABP.FileCore文件服务
依赖包:已安装
Install-Package NPOI -Version 2.6.0
初始化:
1. 引入ABP依赖模块:AbpFileCoreModule
2. 在配置服务容器中添加以下代码
public override void ConfigureServices(ServiceConfigurationContext context)
{
var services = context.Services;
services.AddFileCore(options =>
{
options.AllowFileExts = "允许的文件扩展名集合";
options.LimitMaxSize = "文件最大长度:单位字节";
options.StorageRootDir = "存储文件根目录";
options.MappingUrlPath = "访问文件url地址根路径";
});
}
使用说明:
1.引入依赖注入实例:IFileService
/// <summary>
/// 文件服务
/// </summary>
public interface IFileService
{
/// <summary>
/// 保存文件
/// </summary>
/// <param name="fileData">文件数据</param>
/// <param name="fileName">文件名称</param>
/// <param name="fileRelativeDir">文件的相对目录,会跟存储文件的根目录拼接起来</param>
/// <returns></returns>
SaveFileResultDto SaveFile(byte[] fileData, string fileName, string fileRelativeDir = "");
}
保存文件返回值说明SaveFileResultDto:
FileDir:文件完整存储目录
Url:访问文件的url地址
2.引入依赖注入实例:IExcelService
/// <summary>
/// Excel导入导出
/// </summary>
public interface IExcelService
{
/// <summary>
/// 导出
/// </summary>
/// <typeparam name="TExportDto"></typeparam>
/// <param name="data">数据集合</param>
/// <param name="fileName">文件名称</param>
/// <param name="fileRelativeDir">文件的相对目录,如果传了会跟存储文件的根目录拼接起来</param>
/// <returns></returns>
Task<ExportFileResultDto> ExportAsync<TExportDto>(List<TExportDto> data, string fileName, string fileRelativeDir = "") where TExportDto : class, new();
/// <summary>
/// 导入
/// </summary>
/// <typeparam name="TImportDto"></typeparam>
/// <param name="filePath">导入的文件路径</param>
/// <param name="importFunc">导入成功回调函数</param>
/// <param name="fileName">文件名称</param>
/// <param name="fileRelativeDir">文件的相对目录,会跟存储文件的根目录拼接起来</param>
/// <returns></returns>
Task<ImportFileResultDto> ImportAsync<TImportDto>(string filePath, Func<IEnumerable<TImportDto>, IEnumerable<TImportDto>> importFunc, string fileName, string fileRelativeDir = "") where TImportDto : ImportItemDto, new();
}
导出返回值说明ExportFileResultDto:
FileDir:文件完整存储目录
FileData:文件数据:字节数组
Url:访问文件的url地址
导入返回值说明ImportFileResultDto:
FileDir:导入结果文件完整存储目录
FileData:导入结果文件数据:字节数组
Url:导入结果文件的url地址
Total:导入总记录数
SuccessCount:导入成功记录数
FailCount:导入失败记录数
十四、ABP.PaymentCore在线支付
依赖包:已安装
Install-Package RestSharp -Version 106.13.0
初始化:
1. 引入ABP依赖模块:AbpPaymentCoreModule
2. 在配置服务容器中添加以下代码
public override void ConfigureServices(ServiceConfigurationContext context)
{
var services = context.Services;
//添加空中云汇支付
services.AddAirwallexPayment(options =>
{
options.ClientId = "客户ID:第三方支付平台提供";
options.ApiKey = "调用Api密钥:第三方支付平台提供";
options.GetTokenUrl = "获取Token接口地址";
options.PaymentUrl = "支付下单接口地址";
options.IsRecordRequestMessage = "是否记录请求报文:默认值为true";
options.CacheKeyPrefix = "缓存key前缀:默认值为Airwallex_";
});
}
使用说明:
1.引入依赖注入实例:IPaymentService
/// <summary>
/// 支付服务
/// </summary>
public interface IPaymentService
{
/// <summary>
/// 开始支付:向第三方支付平台下单
/// </summary>
/// <param name="request"></param>
/// <param name="paymentType"></param>
/// <returns></returns>
DirectPayResponse BeginPay(DirectPayRequest request, PaymentTypeEnum paymentType);
/// <summary>
/// 支付完成:解析第三方平台支付成功信息
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
Task<DirectPayResult> PayComplete(HttpContext context);
/// <summary>
/// 支付完成
/// </summary>
/// <param name="packet"></param>
/// <returns></returns>
DirectPayResult PayComplete(HttpPacket packet);
}
1.支付下单参数说明DirectPayRequest:
TradeId:唯一交易ID
TradeName:订单编号
Amount:支付金额
Currency:支付币种
2.支付下单返回值说明DirectPayResponse:
Type:数据类型:由于各平台的支付方式不一样,返回的数据类型也不一样,有的是返回支付链接、有的是返回支付密钥,还有返回支付二维码的
UrlData:支付链接
JsonData:支付密钥
ImageData:二维码数据:字节数组
3.支付成功返回值说明:DirectPayResult
Success:是否支付成功
TradeId:唯一交易ID:我们支付下单的时候提供的
TradeName:订单编号:我们支付下单的时候提供的
PaymentType:支付类型
ThirdPartyTradeId:第三方交易ID
PracticalCurrency:实付币种
PracticalAmount:实付金额
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 is compatible. net5.0-windows was computed. net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
-
net5.0
- AlibabaCloud.SDK.Dysmsapi20170525 (>= 2.0.23)
- Confluent.Kafka (>= 1.9.0)
- EPPlus (>= 4.5.3.3)
- Hangfire (>= 1.8.1)
- Hangfire.Dashboard.BasicAuthorization (>= 1.0.2)
- Hangfire.HttpJob (>= 3.7.6)
- Hangfire.HttpJob.Client (>= 1.2.9)
- Hangfire.Redis.StackExchange (>= 1.8.7)
- JWT (>= 10.0.2)
- LogDashboard (>= 1.4.8)
- NPOI (>= 2.6.0)
- Portable.BouncyCastle (>= 1.9.0)
- protobuf-net (>= 3.1.17)
- RestSharp (>= 106.13.0)
- Serilog.AspNetCore (>= 6.1.0)
- SqlSugarCore (>= 5.1.4.69)
- StackExchange.Redis (>= 2.6.104)
- Swashbuckle.AspNetCore (>= 6.5.0)
- Swashbuckle.AspNetCore.Annotations (>= 6.5.0)
- Volo.Abp.AspNetCore (>= 4.4.4)
- Volo.Abp.AspNetCore.Mvc (>= 4.4.4)
- Volo.Abp.Autofac (>= 4.4.4)
- Volo.Abp.AutoMapper (>= 4.4.4)
-
net6.0
- AlibabaCloud.SDK.Dysmsapi20170525 (>= 2.0.23)
- Confluent.Kafka (>= 2.1.0)
- EPPlus (>= 4.5.3.3)
- Hangfire (>= 1.8.1)
- Hangfire.Dashboard.BasicAuthorization (>= 1.0.2)
- Hangfire.HttpJob (>= 3.7.6)
- Hangfire.HttpJob.Client (>= 1.2.9)
- Hangfire.Redis.StackExchange (>= 1.8.7)
- JWT (>= 10.0.2)
- LogDashboard (>= 1.4.8)
- NPOI (>= 2.6.0)
- Portable.BouncyCastle (>= 1.9.0)
- protobuf-net (>= 3.2.16)
- RestSharp (>= 106.13.0)
- Serilog.AspNetCore (>= 6.1.0)
- SqlSugarCore (>= 5.1.4.69)
- StackExchange.Redis (>= 2.6.104)
- Swashbuckle.AspNetCore (>= 6.5.0)
- Swashbuckle.AspNetCore.Annotations (>= 6.5.0)
- Volo.Abp.AspNetCore (>= 6.0.3)
- Volo.Abp.AspNetCore.Mvc (>= 6.0.3)
- Volo.Abp.Autofac (>= 6.0.3)
- Volo.Abp.AutoMapper (>= 6.0.3)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.