From 2d4ab63deb3e272fb65c54b76001ee61e553147c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sat, 9 May 2020 01:16:56 +0300 Subject: [PATCH] #2747: Initial SignalR Package --- framework/Volo.Abp.sln | 9 +- .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 +++++ .../Properties/launchSettings.json | 27 ++++ .../Volo.Abp.AspNetCore.SignalR.csproj | 23 ++++ .../SignalR/AbpAspNetCoreSignalRModule.cs | 115 ++++++++++++++++++ .../AbpSignalRConventionalRegistrar.cs | 43 +++++++ .../AspNetCore/SignalR/AbpSignalROptions.cs | 14 +++ .../Volo/Abp/AspNetCore/SignalR/HubConfig.cs | 43 +++++++ .../AspNetCore/SignalR/HubRouteAttribute.cs | 33 +++++ nupkg/common.ps1 | 1 + 11 files changed, 340 insertions(+), 1 deletion(-) create mode 100644 framework/src/Volo.Abp.AspNetCore.SignalR/FodyWeavers.xml create mode 100644 framework/src/Volo.Abp.AspNetCore.SignalR/FodyWeavers.xsd create mode 100644 framework/src/Volo.Abp.AspNetCore.SignalR/Properties/launchSettings.json create mode 100644 framework/src/Volo.Abp.AspNetCore.SignalR/Volo.Abp.AspNetCore.SignalR.csproj create mode 100644 framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRModule.cs create mode 100644 framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpSignalRConventionalRegistrar.cs create mode 100644 framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpSignalROptions.cs create mode 100644 framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubConfig.cs create mode 100644 framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubRouteAttribute.cs diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index 5d6f3e99a2..3ff151439f 100644 --- a/framework/Volo.Abp.sln +++ b/framework/Volo.Abp.sln @@ -283,7 +283,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.TextTemplating", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.TextTemplating.Tests", "test\Volo.Abp.TextTemplating.Tests\Volo.Abp.TextTemplating.Tests.csproj", "{251C7FD3-D313-4BCE-8068-352EC7EEA275}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Validation.Abstractions", "src\Volo.Abp.Validation.Abstractions\Volo.Abp.Validation.Abstractions.csproj", "{FA5D1D6A-2A05-4A3D-99C1-2B6C1D1F99A3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Validation.Abstractions", "src\Volo.Abp.Validation.Abstractions\Volo.Abp.Validation.Abstractions.csproj", "{FA5D1D6A-2A05-4A3D-99C1-2B6C1D1F99A3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.SignalR", "src\Volo.Abp.AspNetCore.SignalR\Volo.Abp.AspNetCore.SignalR.csproj", "{B64FCE08-E9D2-4984-BF12-FE199F257416}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -847,6 +849,10 @@ Global {FA5D1D6A-2A05-4A3D-99C1-2B6C1D1F99A3}.Debug|Any CPU.Build.0 = Debug|Any CPU {FA5D1D6A-2A05-4A3D-99C1-2B6C1D1F99A3}.Release|Any CPU.ActiveCfg = Release|Any CPU {FA5D1D6A-2A05-4A3D-99C1-2B6C1D1F99A3}.Release|Any CPU.Build.0 = Release|Any CPU + {B64FCE08-E9D2-4984-BF12-FE199F257416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B64FCE08-E9D2-4984-BF12-FE199F257416}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B64FCE08-E9D2-4984-BF12-FE199F257416}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B64FCE08-E9D2-4984-BF12-FE199F257416}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -991,6 +997,7 @@ Global {9E53F91F-EACD-4191-A487-E727741F1311} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {251C7FD3-D313-4BCE-8068-352EC7EEA275} = {447C8A77-E5F0-4538-8687-7383196D04EA} {FA5D1D6A-2A05-4A3D-99C1-2B6C1D1F99A3} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {B64FCE08-E9D2-4984-BF12-FE199F257416} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/FodyWeavers.xml b/framework/src/Volo.Abp.AspNetCore.SignalR/FodyWeavers.xml new file mode 100644 index 0000000000..be0de3a908 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/FodyWeavers.xsd b/framework/src/Volo.Abp.AspNetCore.SignalR/FodyWeavers.xsd new file mode 100644 index 0000000000..3f3946e282 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.SignalR/Properties/launchSettings.json new file mode 100644 index 0000000000..418c7f2621 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:53900/", + "sslPort": 44362 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Volo.Abp.SignalR": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:5001;http://localhost:5000" + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo.Abp.AspNetCore.SignalR.csproj b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo.Abp.AspNetCore.SignalR.csproj new file mode 100644 index 0000000000..f4c833cb90 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo.Abp.AspNetCore.SignalR.csproj @@ -0,0 +1,23 @@ + + + + + + + netcoreapp3.1 + Volo.Abp.AspNetCore.SignalR + Volo.Abp.AspNetCore.SignalR + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + true + Library + + + + + + + + diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRModule.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRModule.cs new file mode 100644 index 0000000000..e26de184af --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRModule.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http.Connections; +using Microsoft.AspNetCore.Routing; +using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Volo.Abp.Modularity; + +namespace Volo.Abp.AspNetCore.SignalR +{ + [DependsOn( + typeof(AbpAspNetCoreModule) + )] + public class AbpAspNetCoreSignalRModule : AbpModule + { + private static readonly MethodInfo MapHubGenericMethodInfo = + typeof(AbpAspNetCoreSignalRModule) + .GetMethod("MapHub", BindingFlags.Static | BindingFlags.NonPublic); + + public override void PreConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddConventionalRegistrar(new AbpSignalRConventionalRegistrar()); + + AutoAddHubTypes(context.Services); + } + + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddSignalR(); + + Configure(options => + { + options.EndpointConfigureActions.Add(endpointContext => + { + var signalROptions = endpointContext + .ScopeServiceProvider + .GetRequiredService>() + .Value; + + foreach (var hubConfig in signalROptions.Hubs) + { + MapHubType( + hubConfig.HubType, + endpointContext.Endpoints, + hubConfig.RoutePattern, + opts => + { + foreach (var configureAction in hubConfig.ConfigureActions) + { + configureAction(opts); + } + } + ); + } + }); + }); + } + + private void AutoAddHubTypes(IServiceCollection services) + { + var hubTypes = new List(); + + services.OnRegistred(context => + { + if (typeof(Hub).IsAssignableFrom(context.ImplementationType)) + { + hubTypes.Add(context.ImplementationType); + } + }); + + services.Configure(options => + { + foreach (var hubType in hubTypes) + { + options.Hubs.Add(HubConfig.Create(hubType)); + } + }); + } + + private void MapHubType( + Type hubType, + IEndpointRouteBuilder endpoints, + string pattern, + Action configureOptions) + { + MapHubGenericMethodInfo + .MakeGenericMethod(hubType) + .Invoke( + this, + new object[] + { + endpoints, + pattern, + configureOptions + } + ); + } + + // ReSharper disable once UnusedMember.Local (used via reflection) + private static void MapHub( + IEndpointRouteBuilder endpoints, + string pattern, + Action configureOptions) + where THub : Hub + { + endpoints.MapHub( + pattern, + configureOptions + ); + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpSignalRConventionalRegistrar.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpSignalRConventionalRegistrar.cs new file mode 100644 index 0000000000..9dcb46d6bc --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpSignalRConventionalRegistrar.cs @@ -0,0 +1,43 @@ +using System; +using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.AspNetCore.SignalR +{ + public class AbpSignalRConventionalRegistrar : ConventionalRegistrarBase + { + public override void AddType(IServiceCollection services, Type type) + { + if (IsConventionalRegistrationDisabled(type)) + { + return; + } + + if (!IsHub(type)) + { + return; + } + + var serviceTypes = ExposedServiceExplorer.GetExposedServices(type); + + TriggerServiceExposing(services, type, serviceTypes); + + foreach (var serviceType in serviceTypes) + { + services.Add( + ServiceDescriptor.Describe( + serviceType, + type, + ServiceLifetime.Transient + ) + ); + } + } + + private static bool IsHub(Type type) + { + return typeof(Hub).IsAssignableFrom(type); + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpSignalROptions.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpSignalROptions.cs new file mode 100644 index 0000000000..c0a07a2ea3 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpSignalROptions.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace Volo.Abp.AspNetCore.SignalR +{ + public class AbpSignalROptions + { + public List Hubs { get; } + + public AbpSignalROptions() + { + Hubs = new List(); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubConfig.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubConfig.cs new file mode 100644 index 0000000000..59c9342fce --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubConfig.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Http.Connections; +using Microsoft.AspNetCore.SignalR; + +namespace Volo.Abp.AspNetCore.SignalR +{ + public class HubConfig + { + [NotNull] + public Type HubType { get; } + + [NotNull] + public string RoutePattern { get; set; } + + [NotNull] + public List> ConfigureActions { get; set; } + + public HubConfig( + [NotNull] Type hubType, + [NotNull] string routePattern) + { + HubType = Check.NotNull(hubType, nameof(hubType)); + RoutePattern = Check.NotNullOrWhiteSpace(routePattern, nameof(routePattern)); + ConfigureActions = new List>(); + } + + public static HubConfig Create() + where THub : Hub + { + return Create(typeof(THub)); + } + + public static HubConfig Create(Type hubType) + { + return new HubConfig( + hubType, + HubRouteAttribute.GetRoutePattern(hubType) + ); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubRouteAttribute.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubRouteAttribute.cs new file mode 100644 index 0000000000..5de916a89d --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubRouteAttribute.cs @@ -0,0 +1,33 @@ +using System; +using System.Reflection; +using Microsoft.AspNetCore.SignalR; + +namespace Volo.Abp.AspNetCore.SignalR +{ + public class HubRouteAttribute : Attribute + { + public string RoutePattern { get; set; } + + public HubRouteAttribute(string routePattern) + { + RoutePattern = routePattern; + } + + public static string GetRoutePattern() + where THub : Hub + { + return GetRoutePattern(typeof(THub)); + } + + public static string GetRoutePattern(Type hubType) + { + var routeAttribute = hubType.GetSingleAttributeOrNull(); + if (routeAttribute != null) + { + return routeAttribute.RoutePattern; + } + + return "/signalr-hubs/" + hubType.Name.RemovePostFix("Hub").ToKebabCase(); + } + } +} \ No newline at end of file diff --git a/nupkg/common.ps1 b/nupkg/common.ps1 index 6dfe0041aa..661f630a0b 100644 --- a/nupkg/common.ps1 +++ b/nupkg/common.ps1 @@ -41,6 +41,7 @@ $projects = ( "framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared", "framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets", "framework/src/Volo.Abp.AspNetCore.Serilog", + "framework/src/Volo.Abp.AspNetCore.SignalR", "framework/src/Volo.Abp.AspNetCore.TestBase", "framework/src/Volo.Abp.Auditing", "framework/src/Volo.Abp.Authorization",