diff --git a/aspnet-core/LINGYUN.MicroService.Common.sln b/aspnet-core/LINGYUN.MicroService.Common.sln index 3e8024941..dda43482f 100644 --- a/aspnet-core/LINGYUN.MicroService.Common.sln +++ b/aspnet-core/LINGYUN.MicroService.Common.sln @@ -127,6 +127,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Localization.Xm EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Localization.Json.Tests", "tests\LINGYUN.Abp.Localization.Json.Tests\LINGYUN.Abp.Localization.Json.Tests.csproj", "{BA2F4EC9-BC2C-482A-9123-BDACB8B15295}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dapr", "dapr", "{7FDFB22F-1BFF-4E05-9427-78B7A8461D50}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Dapr.Actors", "modules\dapr\LINGYUN.Abp.Dapr.Actors\LINGYUN.Abp.Dapr.Actors.csproj", "{C72E0407-06FE-4CAA-86E5-30FF216A7B82}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Dapr.Actors.IdentityModel", "modules\dapr\LINGYUN.Abp.Dapr.Actors.IdentityModel\LINGYUN.Abp.Dapr.Actors.IdentityModel.csproj", "{A5DC8C25-6504-4C35-A657-7A1BF051570F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Dapr.Actors.IdentityModel.Web", "modules\dapr\LINGYUN.Abp.Dapr.Actors.IdentityModel.Web\LINGYUN.Abp.Dapr.Actors.IdentityModel.Web.csproj", "{E263A9ED-D5DB-4495-A0C7-6268ED92EB92}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Dapr.Actors.AspNetCore", "modules\dapr\LINGYUN.Abp.Dapr.Actors.AspNetCore\LINGYUN.Abp.Dapr.Actors.AspNetCore.csproj", "{E74FF671-6E5E-430C-9211-ED910634DDBE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -333,6 +343,22 @@ Global {BA2F4EC9-BC2C-482A-9123-BDACB8B15295}.Debug|Any CPU.Build.0 = Debug|Any CPU {BA2F4EC9-BC2C-482A-9123-BDACB8B15295}.Release|Any CPU.ActiveCfg = Release|Any CPU {BA2F4EC9-BC2C-482A-9123-BDACB8B15295}.Release|Any CPU.Build.0 = Release|Any CPU + {C72E0407-06FE-4CAA-86E5-30FF216A7B82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C72E0407-06FE-4CAA-86E5-30FF216A7B82}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C72E0407-06FE-4CAA-86E5-30FF216A7B82}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C72E0407-06FE-4CAA-86E5-30FF216A7B82}.Release|Any CPU.Build.0 = Release|Any CPU + {A5DC8C25-6504-4C35-A657-7A1BF051570F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A5DC8C25-6504-4C35-A657-7A1BF051570F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A5DC8C25-6504-4C35-A657-7A1BF051570F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A5DC8C25-6504-4C35-A657-7A1BF051570F}.Release|Any CPU.Build.0 = Release|Any CPU + {E263A9ED-D5DB-4495-A0C7-6268ED92EB92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E263A9ED-D5DB-4495-A0C7-6268ED92EB92}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E263A9ED-D5DB-4495-A0C7-6268ED92EB92}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E263A9ED-D5DB-4495-A0C7-6268ED92EB92}.Release|Any CPU.Build.0 = Release|Any CPU + {E74FF671-6E5E-430C-9211-ED910634DDBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E74FF671-6E5E-430C-9211-ED910634DDBE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E74FF671-6E5E-430C-9211-ED910634DDBE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E74FF671-6E5E-430C-9211-ED910634DDBE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -398,6 +424,11 @@ Global {8CC72F4E-F134-4A43-9037-5D4D1F29B68A} = {E73A0F8B-2B4B-4CED-82A4-1EE5E0B89744} {94FEA59E-3B6D-41A0-9E44-BA5D6477244F} = {B86C21A4-73B7-471E-B73A-B4B905EC9435} {BA2F4EC9-BC2C-482A-9123-BDACB8B15295} = {B86C21A4-73B7-471E-B73A-B4B905EC9435} + {7FDFB22F-1BFF-4E05-9427-78B7A8461D50} = {02EA4E78-5891-43BC-944F-3E52FEE032E4} + {C72E0407-06FE-4CAA-86E5-30FF216A7B82} = {7FDFB22F-1BFF-4E05-9427-78B7A8461D50} + {A5DC8C25-6504-4C35-A657-7A1BF051570F} = {7FDFB22F-1BFF-4E05-9427-78B7A8461D50} + {E263A9ED-D5DB-4495-A0C7-6268ED92EB92} = {7FDFB22F-1BFF-4E05-9427-78B7A8461D50} + {E74FF671-6E5E-430C-9211-ED910634DDBE} = {7FDFB22F-1BFF-4E05-9427-78B7A8461D50} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {06C707C6-02C0-411A-AD3B-2D0E13787CB8} diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore/Dapr/Actors/Runtime/ActorRegistrationExtensions.cs b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore/Dapr/Actors/Runtime/ActorRegistrationExtensions.cs new file mode 100644 index 000000000..e7f481faa --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore/Dapr/Actors/Runtime/ActorRegistrationExtensions.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Dapr.Actors.Runtime +{ + public static class ActorRegistrationExtensions + { + public static bool Contains( + this ICollection registrations, + Type implementationType) + { + return registrations.Any(x => x.Type.ImplementationType == implementationType); + } + } +} diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore/LINGYUN.Abp.Dapr.Actors.AspNetCore.csproj b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore/LINGYUN.Abp.Dapr.Actors.AspNetCore.csproj new file mode 100644 index 000000000..270cebc66 --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore/LINGYUN.Abp.Dapr.Actors.AspNetCore.csproj @@ -0,0 +1,15 @@ + + + + + + net5.0 + + + + + + + + + diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore/LINGYUN/Abp/Dapr/Actors/AspNetCore/AbpDaprActorsAspNetCoreModule.cs b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore/LINGYUN/Abp/Dapr/Actors/AspNetCore/AbpDaprActorsAspNetCoreModule.cs new file mode 100644 index 000000000..fa267ada0 --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore/LINGYUN/Abp/Dapr/Actors/AspNetCore/AbpDaprActorsAspNetCoreModule.cs @@ -0,0 +1,54 @@ +using Dapr.Actors; +using Dapr.Actors.Runtime; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using Volo.Abp.AspNetCore; +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.Dapr.Actors.AspNetCore +{ + [DependsOn( + typeof(AbpAspNetCoreModule))] + public class AbpDaprActorsAspNetCoreModule : AbpModule + { + public override void PreConfigureServices(ServiceConfigurationContext context) + { + AddDefinitionActor(context.Services); + } + + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.EndpointConfigureActions.Add(endpointContext => + { + endpointContext.Endpoints.MapActorsHandlers(); + }); + }); + } + + private static void AddDefinitionActor(IServiceCollection services) + { + var actorRegistrations = new List(); + + services.OnRegistred(context => + { + if (typeof(IActor).IsAssignableFrom(context.ImplementationType) && + !actorRegistrations.Contains(context.ImplementationType)) + { + var actorRegistration = new ActorRegistration(context.ImplementationType.GetActorTypeInfo()); + + actorRegistrations.Add(actorRegistration); + } + }); + + services.AddActors(options => + { + options.Actors.AddIfNotContains(actorRegistrations); + }); + } + } +} diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore/README.md b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore/README.md new file mode 100644 index 000000000..f1e15535c --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore/README.md @@ -0,0 +1,19 @@ +# LINGYUN.Abp.Dapr.Actors.AspNetCore + +Dapr.Asp.NetCore的Abp框架集成,扫描程序集内部实现的Actor服务列表,批量注册为Dapr.Actors + +## 配置使用 + +模块按需引用 + +```csharp +[DependsOn(typeof(AbpDaprActorsAspNetCoreModule))] +public class YouProjectModule : AbpModule +{ + // other +} +``` +## 配置项说明 + + +## 其他 diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore/System/TypeExtensions.cs b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore/System/TypeExtensions.cs new file mode 100644 index 000000000..bbc95f608 --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore/System/TypeExtensions.cs @@ -0,0 +1,100 @@ +using Dapr.Actors; +using Dapr.Actors.Runtime; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Volo.Abp; + +namespace System +{ + internal static class TypeExtensions + { + public static bool IsActor(this Type actorType) + { + Type baseType = actorType.GetTypeInfo().BaseType; + while (baseType != null) + { + if (baseType == typeof(Actor)) + { + return true; + } + + actorType = baseType; + baseType = actorType.GetTypeInfo().BaseType; + } + + return false; + } + + public static Type[] GetActorInterfaces(this Type type) + { + List list = new List(from t in type.GetInterfaces() + where typeof(IActor).IsAssignableFrom(t) + select t); + list.RemoveAll((Type t) => t.GetNonActorParentType() != null); + return list.ToArray(); + } + + public static RemoteServiceAttribute GetRemoteServiceAttribute(this Type type) + { + return type.GetInterfaces() + .Where(t => t.IsDefined(typeof(RemoteServiceAttribute), false)) + .Select(t => t.GetCustomAttribute()) + .FirstOrDefault(); + } + + public static Type GetNonActorParentType(this Type type) + { + List list = new List(type.GetInterfaces()); + if (list.RemoveAll((Type t) => t == typeof(IActor)) == 0) + { + return type; + } + + foreach (Type item in list) + { + Type nonActorParentType = item.GetNonActorParentType(); + if (nonActorParentType != null) + { + return nonActorParentType; + } + } + + return null; + } + + public static ActorTypeInformation GetActorTypeInfo( + this Type actorType) + { + if (!actorType.IsActor()) + { + throw new ArgumentException( + string.Format("The type '{0}' is not an Actor. An actor type must derive from '{1}'.", actorType.FullName, typeof(Actor).FullName), + nameof(actorType)); + } + + Type[] actorInterfaces = actorType.GetActorInterfaces(); + if (actorInterfaces.Length == 0 && !actorType.GetTypeInfo().IsAbstract) + { + throw new ArgumentException( + string.Format("The actor type '{0}' does not implement any actor interfaces or one of the interfaces implemented is not an actor interface." + + " All interfaces(including its parent interface) implemented by actor type must be actor interface. " + + "An actor interface is the one that ultimately derives from '{1}' type.", actorType.FullName, typeof(IActor).FullName), + nameof(actorType)); + } + + var actorTypeInfo = ActorTypeInformation.Get(actorType); + + var remoteServiceAttr = actorType.GetRemoteServiceAttribute(); + if (remoteServiceAttr != null && + !string.Equals(actorTypeInfo.ActorTypeName, remoteServiceAttr.Name)) + { + typeof(ActorTypeInformation) + .GetProperty(nameof(ActorTypeInformation.ActorTypeName), BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) + ?.SetValue(actorTypeInfo, remoteServiceAttr.Name); + } + + return actorTypeInfo; + } + } +} diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel.Web/LINGYUN.Abp.Dapr.Actors.IdentityModel.Web.csproj b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel.Web/LINGYUN.Abp.Dapr.Actors.IdentityModel.Web.csproj new file mode 100644 index 000000000..b40c23d89 --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel.Web/LINGYUN.Abp.Dapr.Actors.IdentityModel.Web.csproj @@ -0,0 +1,20 @@ + + + + + + net5.0 + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + true + Library + + + + + + + + diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel.Web/LINGYUN.Abp.Dapr.Actors.IdentityModel.Web.csproj.user b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel.Web/LINGYUN.Abp.Dapr.Actors.IdentityModel.Web.csproj.user new file mode 100644 index 000000000..cff74a90e --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel.Web/LINGYUN.Abp.Dapr.Actors.IdentityModel.Web.csproj.user @@ -0,0 +1,6 @@ + + + + IIS Express + + \ No newline at end of file diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel.Web/LINGYUN/Abp/Dapr/Actors/IdentityModel/Web/AbpDaprActorsIdentityModelWebModule.cs b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel.Web/LINGYUN/Abp/Dapr/Actors/IdentityModel/Web/AbpDaprActorsIdentityModelWebModule.cs new file mode 100644 index 000000000..068dbc8f7 --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel.Web/LINGYUN/Abp/Dapr/Actors/IdentityModel/Web/AbpDaprActorsIdentityModelWebModule.cs @@ -0,0 +1,10 @@ +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.Dapr.Actors.IdentityModel.Web +{ + [DependsOn( + typeof(AbpDaprActorsIdentityModelModule))] + public class AbpDaprActorsIdentityModelWebModule : AbpModule + { + } +} diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel.Web/LINGYUN/Abp/Dapr/Actors/IdentityModel/Web/HttpContextIdentityModelDaprActorProxyAuthenticator.cs b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel.Web/LINGYUN/Abp/Dapr/Actors/IdentityModel/Web/HttpContextIdentityModelDaprActorProxyAuthenticator.cs new file mode 100644 index 000000000..2f4cc8dab --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel.Web/LINGYUN/Abp/Dapr/Actors/IdentityModel/Web/HttpContextIdentityModelDaprActorProxyAuthenticator.cs @@ -0,0 +1,51 @@ +using LINGYUN.Abp.Dapr.Actors.Authentication; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.IdentityModel; + +namespace LINGYUN.Abp.Dapr.Actors.IdentityModel +{ + [Dependency(ReplaceServices = true)] + public class HttpContextIdentityModelDaprActorProxyAuthenticator : IdentityModelDaprActorProxyAuthenticator + { + public IHttpContextAccessor HttpContextAccessor { get; set; } + + public HttpContextIdentityModelDaprActorProxyAuthenticator( + IIdentityModelAuthenticationService identityModelAuthenticationService) + : base(identityModelAuthenticationService) + { + } + + public override async Task AuthenticateAsync(DaprActorProxyAuthenticateContext context) + { + if (context.RemoteService.GetUseCurrentAccessToken() != false) + { + var accessToken = await GetAccessTokenFromHttpContextOrNullAsync(); + if (accessToken != null) + { + context.Handler.PreConfigure(request => + { + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); + }); + return; + } + } + + await base.AuthenticateAsync(context); + } + + protected virtual async Task GetAccessTokenFromHttpContextOrNullAsync() + { + var httpContext = HttpContextAccessor?.HttpContext; + if (httpContext == null) + { + return null; + } + + return await httpContext.GetTokenAsync("access_token"); + } + } +} diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel.Web/Properties/launchSettings.json b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel.Web/Properties/launchSettings.json new file mode 100644 index 000000000..89ce41cf2 --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel.Web/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:51684/", + "sslPort": 44331 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "LINGYUN.Abp.Dapr.Actors.IdentityModel.Web": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:5001;http://localhost:5000" + } + } +} \ No newline at end of file diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel.Web/README.md b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel.Web/README.md new file mode 100644 index 000000000..359063b86 --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel.Web/README.md @@ -0,0 +1,19 @@ +# LINGYUN.Abp.Dapr.Actors.IdentityModel.Web + +Dapr.Actors内部使用Http进行服务间调用,此模块用于获取应用当前状态中的身份令牌并传递到远程Actor服务 + +## 配置使用 + +模块按需引用 + +```csharp +[DependsOn(typeof(AbpDaprActorsIdentityModelWebModule))] +public class YouProjectModule : AbpModule +{ + // other +} +``` +## 配置项说明 + + +## 其他 diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel/LINGYUN.Abp.Dapr.Actors.IdentityModel.csproj b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel/LINGYUN.Abp.Dapr.Actors.IdentityModel.csproj new file mode 100644 index 000000000..94dd16055 --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel/LINGYUN.Abp.Dapr.Actors.IdentityModel.csproj @@ -0,0 +1,18 @@ + + + + + + net5.0 + + + + + + + + + + + + diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel/LINGYUN/Abp/Dapr/Actors/DaprActorConfigurationExtensions.cs b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel/LINGYUN/Abp/Dapr/Actors/DaprActorConfigurationExtensions.cs new file mode 100644 index 000000000..481ea1290 --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel/LINGYUN/Abp/Dapr/Actors/DaprActorConfigurationExtensions.cs @@ -0,0 +1,54 @@ +using JetBrains.Annotations; +using System.Collections.Generic; +using Volo.Abp; + +namespace LINGYUN.Abp.Dapr.Actors +{ + public static class DaprActorConfigurationExtensions + { + public const string IdentityClientName = "IdentityClient"; + public const string UseCurrentAccessTokenName = "UseCurrentAccessToken"; + + [CanBeNull] + public static string GetIdentityClient([NotNull] this DaprActorConfiguration configuration) + { + Check.NotNullOrEmpty(configuration, nameof(configuration)); + + return configuration.GetOrDefault(IdentityClientName); + } + + public static DaprActorConfiguration SetIdentityClient([NotNull] this DaprActorConfiguration configuration, [CanBeNull] string value) + { + configuration[IdentityClientName] = value; + return configuration; + } + + [CanBeNull] + public static bool? GetUseCurrentAccessToken([NotNull] this DaprActorConfiguration configuration) + { + Check.NotNullOrEmpty(configuration, nameof(configuration)); + + var value = configuration.GetOrDefault(UseCurrentAccessTokenName); + if (value == null) + { + return null; + } + + return bool.Parse(value); + } + + public static DaprActorConfiguration SetUseCurrentAccessToken([NotNull] this DaprActorConfiguration configuration, [CanBeNull] bool? value) + { + if (value == null) + { + configuration.Remove(UseCurrentAccessTokenName); + } + else + { + configuration[UseCurrentAccessTokenName] = value.Value.ToString().ToLowerInvariant(); + } + + return configuration; + } + } +} diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel/LINGYUN/Abp/Dapr/Actors/IdentityModel/AbpDaprActorsIdentityModelModule.cs b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel/LINGYUN/Abp/Dapr/Actors/IdentityModel/AbpDaprActorsIdentityModelModule.cs new file mode 100644 index 000000000..d0d79453f --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel/LINGYUN/Abp/Dapr/Actors/IdentityModel/AbpDaprActorsIdentityModelModule.cs @@ -0,0 +1,13 @@ +using Volo.Abp.IdentityModel; +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.Dapr.Actors.IdentityModel +{ + [DependsOn( + typeof(AbpDaprActorsModule), + typeof(AbpIdentityModelModule) + )] + public class AbpDaprActorsIdentityModelModule : AbpModule + { + } +} diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel/LINGYUN/Abp/Dapr/Actors/IdentityModel/IdentityModelDaprActorProxyAuthenticator.cs b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel/LINGYUN/Abp/Dapr/Actors/IdentityModel/IdentityModelDaprActorProxyAuthenticator.cs new file mode 100644 index 000000000..f2c238ac0 --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel/LINGYUN/Abp/Dapr/Actors/IdentityModel/IdentityModelDaprActorProxyAuthenticator.cs @@ -0,0 +1,66 @@ +using LINGYUN.Abp.Dapr.Actors.Authentication; +using LINGYUN.Abp.Dapr.Actors.DynamicProxying; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using System; +using System.Collections.Generic; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.IdentityModel; + +namespace LINGYUN.Abp.Dapr.Actors.IdentityModel +{ + [Dependency(ReplaceServices = true)] + public class IdentityModelDaprActorProxyAuthenticator : IDaprActorProxyAuthenticator, ITransientDependency + { + protected AbpIdentityClientOptions ClientOptions { get; } + protected IIdentityModelAuthenticationService IdentityModelAuthenticationService { get; } + + public ILogger Logger { get; set; } + + public IdentityModelDaprActorProxyAuthenticator( + IIdentityModelAuthenticationService identityModelAuthenticationService) + { + IdentityModelAuthenticationService = identityModelAuthenticationService; + Logger = NullLogger.Instance; + } + + public virtual async Task AuthenticateAsync(DaprActorProxyAuthenticateContext context) + { + var identityClientName = context.RemoteService.GetIdentityClient(); + var configuration = GetClientConfiguration(identityClientName); + if (configuration == null) + { + Logger.LogWarning($"Could not find {nameof(IdentityClientConfiguration)} for {identityClientName}. Either define a configuration for {identityClientName} or set a default configuration."); + return; + } + var accessToken = await IdentityModelAuthenticationService.GetAccessTokenAsync(configuration); + if (accessToken == null) + { + return; + } + + SetAccessToken(context.Handler, accessToken); + } + + protected virtual void SetAccessToken(DaprHttpClientHandler handler, string accessToken) + { + handler.PreConfigure(request => + { + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); + }); + } + + private IdentityClientConfiguration GetClientConfiguration(string identityClientName = null) + { + if (identityClientName.IsNullOrEmpty()) + { + return ClientOptions.IdentityClients.Default; + } + + return ClientOptions.IdentityClients.GetOrDefault(identityClientName) ?? + ClientOptions.IdentityClients.Default; + } + } +} diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel/README.md b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel/README.md new file mode 100644 index 000000000..28dd4a3f4 --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors.IdentityModel/README.md @@ -0,0 +1,19 @@ +# LINGYUN.Abp.Dapr.Actors.IdentityModel + +Dapr.Actors内部使用Http进行服务间调用,此模块用于传递服务间调用令牌 + +## 配置使用 + +模块按需引用 + +```csharp +[DependsOn(typeof(AbpDaprActorsIdentityModelModule))] +public class YouProjectModule : AbpModule +{ + // other +} +``` +## 配置项说明 + + +## 其他 diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN.Abp.Dapr.Actors.csproj b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN.Abp.Dapr.Actors.csproj new file mode 100644 index 000000000..8e501c9f3 --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN.Abp.Dapr.Actors.csproj @@ -0,0 +1,17 @@ + + + + + + net5.0 + + + + + + + + + + + diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/AbpDaprActorCallException.cs b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/AbpDaprActorCallException.cs new file mode 100644 index 000000000..8ce89ad5f --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/AbpDaprActorCallException.cs @@ -0,0 +1,41 @@ +using System.Runtime.Serialization; +using Volo.Abp; +using Volo.Abp.ExceptionHandling; +using Volo.Abp.Http; + +namespace LINGYUN.Abp.Dapr.Actors +{ + public class AbpDaprActorCallException : AbpException, IHasErrorCode, IHasErrorDetails + { + public string Code => Error?.Code; + + public string Details => Error?.Details; + + public RemoteServiceErrorInfo Error { get; set; } + + public AbpDaprActorCallException() + { + + } + + public AbpDaprActorCallException(SerializationInfo serializationInfo, StreamingContext context) + : base(serializationInfo, context) + { + + } + + public AbpDaprActorCallException(RemoteServiceErrorInfo error) + : base(error.Message) + { + Error = error; + + if (error.Data != null) + { + foreach (var dataKey in error.Data.Keys) + { + Data[dataKey] = error.Data[dataKey]; + } + } + } + } +} diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/AbpDaprActorOptions.cs b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/AbpDaprActorOptions.cs new file mode 100644 index 000000000..eb06d987b --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/AbpDaprActorOptions.cs @@ -0,0 +1,12 @@ +namespace LINGYUN.Abp.Dapr.Actors +{ + public class AbpDaprActorOptions + { + public DaprActorConfigurationDictionary RemoteActors { get; set; } + + public AbpDaprActorOptions() + { + RemoteActors = new DaprActorConfigurationDictionary(); + } + } +} diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/AbpDaprActorProxyOptions.cs b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/AbpDaprActorProxyOptions.cs new file mode 100644 index 000000000..8fd9331c7 --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/AbpDaprActorProxyOptions.cs @@ -0,0 +1,19 @@ +using LINGYUN.Abp.Dapr.Actors.DynamicProxying; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.Dapr.Actors +{ + public class AbpDaprActorProxyOptions + { + public Dictionary ActorProxies { get; set; } + + public AbpDaprActorProxyOptions() + { + ActorProxies = new Dictionary(); + } + } +} diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/AbpDaprActorsModule.cs b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/AbpDaprActorsModule.cs new file mode 100644 index 000000000..d9ae68fba --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/AbpDaprActorsModule.cs @@ -0,0 +1,24 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Castle; +using Volo.Abp.ExceptionHandling; +using Volo.Abp.Modularity; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Validation; + +namespace LINGYUN.Abp.Dapr.Actors +{ + [DependsOn( + typeof(AbpCastleCoreModule), + typeof(AbpMultiTenancyModule), + typeof(AbpValidationModule), + typeof(AbpExceptionHandlingModule) + )] + public class AbpDaprActorsModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + var configuration = context.Services.GetConfiguration(); + Configure(configuration); + } + } +} diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/Authentication/DaprActorProxyAuthenticateContext.cs b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/Authentication/DaprActorProxyAuthenticateContext.cs new file mode 100644 index 000000000..db63deed0 --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/Authentication/DaprActorProxyAuthenticateContext.cs @@ -0,0 +1,21 @@ +using LINGYUN.Abp.Dapr.Actors.DynamicProxying; + +namespace LINGYUN.Abp.Dapr.Actors.Authentication +{ + public class DaprActorProxyAuthenticateContext + { + public DaprHttpClientHandler Handler { get; } + public DaprActorConfiguration RemoteService { get; } + + public string RemoteServiceName { get; } + public DaprActorProxyAuthenticateContext( + DaprHttpClientHandler handler, + DaprActorConfiguration remoteService, + string remoteServiceName) + { + Handler = handler; + RemoteService = remoteService; + RemoteServiceName = remoteServiceName; + } + } +} diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/Authentication/IDaprActorProxyAuthenticator.cs b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/Authentication/IDaprActorProxyAuthenticator.cs new file mode 100644 index 000000000..71a465f46 --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/Authentication/IDaprActorProxyAuthenticator.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace LINGYUN.Abp.Dapr.Actors.Authentication +{ + public interface IDaprActorProxyAuthenticator + { + Task AuthenticateAsync(DaprActorProxyAuthenticateContext context); + } +} diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/Authentication/NullDaprActorProxyAuthenticator.cs b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/Authentication/NullDaprActorProxyAuthenticator.cs new file mode 100644 index 000000000..c2945e5ac --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/Authentication/NullDaprActorProxyAuthenticator.cs @@ -0,0 +1,14 @@ +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace LINGYUN.Abp.Dapr.Actors.Authentication +{ + [Dependency(TryRegister = true)] + public class NullDaprActorProxyAuthenticator : IDaprActorProxyAuthenticator, ISingletonDependency + { + public Task AuthenticateAsync(DaprActorProxyAuthenticateContext context) + { + return Task.CompletedTask; + } + } +} diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/DaprActorConfiguration.cs b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/DaprActorConfiguration.cs new file mode 100644 index 000000000..4bc66132b --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/DaprActorConfiguration.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; + +namespace LINGYUN.Abp.Dapr.Actors +{ + public class DaprActorConfiguration : Dictionary + { + /// + /// Base ActorId. + /// + public string ActorId + { + get => this.GetOrDefault(nameof(ActorId)); + set => this[nameof(ActorId)] = value; + } + + /// + /// Base Url. + /// + public string BaseUrl + { + get => this.GetOrDefault(nameof(BaseUrl)); + set => this[nameof(BaseUrl)] = value; + } + + public DaprActorConfiguration() + { + } + + public DaprActorConfiguration( + string actorId, + string baseUrl) + { + this[nameof(ActorId)] = actorId; + this[nameof(BaseUrl)] = baseUrl; + } + } +} diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/DaprActorConfigurationDictionary.cs b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/DaprActorConfigurationDictionary.cs new file mode 100644 index 000000000..d0e9198b6 --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/DaprActorConfigurationDictionary.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using Volo.Abp; + +namespace LINGYUN.Abp.Dapr.Actors +{ + public class DaprActorConfigurationDictionary : Dictionary + { + public const string DefaultName = "Default"; + + public DaprActorConfiguration Default + { + get => this.GetOrDefault(DefaultName); + set => this[DefaultName] = value; + } + + public DaprActorConfiguration GetConfigurationOrDefault(string name) + { + return this.GetOrDefault(name) + ?? Default + ?? throw new AbpException($"Dapr service '{name}' was not found and there is no default configuration."); + } + } +} diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/DynamicProxying/DaprHttpClientHandler.cs b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/DynamicProxying/DaprHttpClientHandler.cs new file mode 100644 index 000000000..5b376e8b8 --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/DynamicProxying/DaprHttpClientHandler.cs @@ -0,0 +1,40 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.Dapr.Actors.DynamicProxying +{ + public class DaprHttpClientHandler : HttpClientHandler + { + private Action _preConfigureInvoke; + protected Action PreConfigureInvoke => _preConfigureInvoke; + + public virtual void PreConfigure(Action config) + { + if (_preConfigureInvoke == null) + { + _preConfigureInvoke = config; + } + else + { + _preConfigureInvoke += config; + } + } + + public void AddHeader(string key, string value) + { + PreConfigure(request => + { + request.Headers.Add(key, value); + }); + } + + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + PreConfigureInvoke?.Invoke(request); + + return await base.SendAsync(request, cancellationToken); + } + } +} diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/DynamicProxying/DynamicDaprActorProxyConfig.cs b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/DynamicProxying/DynamicDaprActorProxyConfig.cs new file mode 100644 index 000000000..7dfb41a90 --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/DynamicProxying/DynamicDaprActorProxyConfig.cs @@ -0,0 +1,17 @@ +using System; + +namespace LINGYUN.Abp.Dapr.Actors.DynamicProxying +{ + public class DynamicDaprActorProxyConfig + { + public Type Type { get; } + + public string RemoteServiceName { get; } + + public DynamicDaprActorProxyConfig(Type type, string remoteServiceName) + { + Type = type; + RemoteServiceName = remoteServiceName; + } + } +} diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/DynamicProxying/DynamicDaprActorProxyInterceptor.cs b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/DynamicProxying/DynamicDaprActorProxyInterceptor.cs new file mode 100644 index 000000000..20024bc41 --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/LINGYUN/Abp/Dapr/Actors/DynamicProxying/DynamicDaprActorProxyInterceptor.cs @@ -0,0 +1,153 @@ +using Dapr.Actors; +using Dapr.Actors.Client; +using LINGYUN.Abp.Dapr.Actors.Authentication; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Reflection; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.DependencyInjection; +using Volo.Abp.DynamicProxy; +using Volo.Abp.Http; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Threading; + +namespace LINGYUN.Abp.Dapr.Actors.DynamicProxying +{ + public class DynamicDaprActorProxyInterceptor : AbpInterceptor, ITransientDependency + where TService: IActor + { + protected ICurrentTenant CurrentTenant { get; } + protected AbpDaprActorOptions DaprActorOptions { get; } + protected AbpDaprActorProxyOptions DaprActorProxyOptions { get; } + protected IDaprActorProxyAuthenticator ActoryProxyAuthenticator { get; } + + public ILogger> Logger { get; set; } + + public DynamicDaprActorProxyInterceptor( + IOptions daprActorProxyOptions, + IOptionsSnapshot daprActorOptions, + IDaprActorProxyAuthenticator actorProxyAuthenticator, + ICurrentTenant currentTenant) + { + CurrentTenant = currentTenant; + ActoryProxyAuthenticator = actorProxyAuthenticator; + DaprActorProxyOptions = daprActorProxyOptions.Value; + DaprActorOptions = daprActorOptions.Value; + + Logger = NullLogger>.Instance; + } + + public override async Task InterceptAsync(IAbpMethodInvocation invocation) + { + await MakeRequestAsync(invocation); + } + + private async Task MakeRequestAsync(IAbpMethodInvocation invocation) + { + // 获取Actor配置 + var actorProxyConfig = DaprActorProxyOptions.ActorProxies.GetOrDefault(typeof(TService)) ?? throw new AbpException($"Could not get DynamicDaprActorProxyConfig for {typeof(TService).FullName}."); + var remoteServiceConfig = DaprActorOptions.RemoteActors.GetConfigurationOrDefault(actorProxyConfig.RemoteServiceName); + + var actorProxyOptions = new ActorProxyOptions + { + HttpEndpoint = remoteServiceConfig.BaseUrl + }; + + // 自定义请求处理器 + // 可添加请求头 + var httpClientHandler = new DaprHttpClientHandler(); + + // 身份认证处理 + await ActoryProxyAuthenticator.AuthenticateAsync( + new DaprActorProxyAuthenticateContext( + httpClientHandler, remoteServiceConfig, actorProxyConfig.RemoteServiceName)); + + AddHeaders(httpClientHandler); + + // 构建代理服务 + var proxyFactory = new ActorProxyFactory(actorProxyOptions, (HttpMessageHandler)httpClientHandler); + + await MakeRequestAsync(invocation, proxyFactory, remoteServiceConfig); + } + + private async Task MakeRequestAsync( + IAbpMethodInvocation invocation, + ActorProxyFactory proxyFactory, + DaprActorConfiguration configuration + ) + { + var actorId = new ActorId(configuration.ActorId); + + var invokeType = typeof(TService); + var remoteServiceAttr = invokeType.GetTypeInfo().GetCustomAttribute(); + var actorType = remoteServiceAttr != null + ? remoteServiceAttr.Name + : invokeType.Name; + var isAsyncMethod = invocation.Method.IsAsync(); + + try + { + var actorProxy = proxyFactory.CreateActorProxy(actorId, actorType); + if (isAsyncMethod) + { + // 调用异步Actor + var task = (Task)invocation.Method.Invoke(actorProxy, invocation.Arguments); + await task; + + if (!invocation.Method.ReturnType.GenericTypeArguments.IsNullOrEmpty()) + { + // 处理返回值 + invocation.ReturnValue = typeof(Task<>) + .MakeGenericType(invocation.Method.ReturnType.GenericTypeArguments[0]) + .GetProperty(nameof(Task.Result), BindingFlags.Public | BindingFlags.Instance) + .GetValue(task); + } + } + else + { + // 调用同步Actor + invocation.ReturnValue = invocation.Method.Invoke(actorProxy, invocation.Arguments); + } + } + catch (ActorMethodInvocationException amie) // 其他异常忽略交给框架处理 + { + if (amie.InnerException != null && amie.InnerException is ActorInvokeException aie) + { + // Dapr 包装了远程服务异常 + throw new AbpDaprActorCallException( + new RemoteServiceErrorInfo + { + Message = aie.Message, + Code = aie.ActualExceptionType + } + ); + } + throw; + } + } + + private void AddHeaders(DaprHttpClientHandler handler) + { + //TenantId + if (CurrentTenant.Id.HasValue) + { + //TODO: Use AbpAspNetCoreMultiTenancyOptions to get the key + handler.AddHeader(TenantResolverConsts.DefaultTenantKey, CurrentTenant.Id.Value.ToString()); + } + //Culture + //TODO: Is that the way we want? Couldn't send the culture (not ui culture) + var currentCulture = CultureInfo.CurrentUICulture.Name ?? CultureInfo.CurrentCulture.Name; + if (!currentCulture.IsNullOrEmpty()) + { + handler.PreConfigure(request => request.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue(currentCulture))); + } + } + } +} diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/Microsoft/Extensions/DependencyInjection/ServiceCollectionDynamicDaprActorProxyExtensions.cs b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/Microsoft/Extensions/DependencyInjection/ServiceCollectionDynamicDaprActorProxyExtensions.cs new file mode 100644 index 000000000..0163be7e3 --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/Microsoft/Extensions/DependencyInjection/ServiceCollectionDynamicDaprActorProxyExtensions.cs @@ -0,0 +1,104 @@ +using Castle.DynamicProxy; +using Dapr.Actors; +using JetBrains.Annotations; +using LINGYUN.Abp.Dapr.Actors; +using LINGYUN.Abp.Dapr.Actors.DynamicProxying; +using System; +using System.Linq; +using System.Reflection; +using Volo.Abp; +using Volo.Abp.Castle.DynamicProxy; +using Volo.Abp.Validation; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class ServiceCollectionDynamicDaprActorProxyExtensions + { + private static readonly ProxyGenerator ProxyGeneratorInstance = new ProxyGenerator(); + + public static IServiceCollection AddDaprActorProxies( + [NotNull] this IServiceCollection services, + [NotNull] Assembly assembly, + [NotNull] string remoteServiceConfigurationName = DaprActorConfigurationDictionary.DefaultName, + bool asDefaultServices = true) + { + Check.NotNull(services, nameof(assembly)); + + var serviceTypes = assembly.GetTypes().Where(IsSuitableForDynamicActorProxying).ToArray(); + + foreach (var serviceType in serviceTypes) + { + services.AddDaprActorProxy( + serviceType, + remoteServiceConfigurationName, + asDefaultServices + ); + } + + return services; + } + + public static IServiceCollection AddDaprActorProxy( + [NotNull] this IServiceCollection services, + [NotNull] string remoteServiceConfigurationName = DaprActorConfigurationDictionary.DefaultName, + bool asDefaultService = true) + { + return services.AddDaprActorProxy( + typeof(T), + remoteServiceConfigurationName, + asDefaultService + ); + } + + public static IServiceCollection AddDaprActorProxy( + [NotNull] this IServiceCollection services, + [NotNull] Type type, + [NotNull] string remoteServiceConfigurationName = DaprActorConfigurationDictionary.DefaultName, + bool asDefaultService = true) + { + Check.NotNull(services, nameof(services)); + Check.NotNull(type, nameof(type)); + Check.NotNullOrWhiteSpace(remoteServiceConfigurationName, nameof(remoteServiceConfigurationName)); + + // AddHttpClientFactory(services, remoteServiceConfigurationName); + + services.Configure(options => + { + options.ActorProxies[type] = new DynamicDaprActorProxyConfig(type, remoteServiceConfigurationName); + }); + + var interceptorType = typeof(DynamicDaprActorProxyInterceptor<>).MakeGenericType(type); + services.AddTransient(interceptorType); + + var interceptorAdapterType = typeof(AbpAsyncDeterminationInterceptor<>).MakeGenericType(interceptorType); + + var validationInterceptorAdapterType = + typeof(AbpAsyncDeterminationInterceptor<>).MakeGenericType(typeof(ValidationInterceptor)); + + if (asDefaultService) + { + services.AddTransient( + type, + serviceProvider => ProxyGeneratorInstance + .CreateInterfaceProxyWithoutTarget( + type, + (IInterceptor)serviceProvider.GetRequiredService(validationInterceptorAdapterType), + (IInterceptor)serviceProvider.GetRequiredService(interceptorAdapterType) + ) + ); + } + + return services; + } + + private static bool IsSuitableForDynamicActorProxying(Type type) + { + //TODO: Add option to change type filter + + return type.IsInterface + && type.IsPublic + && !type.IsGenericType + && typeof(IActor).IsAssignableFrom(type); + } + } +} diff --git a/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/README.md b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/README.md new file mode 100644 index 000000000..b0c63535d --- /dev/null +++ b/aspnet-core/modules/dapr/LINGYUN.Abp.Dapr.Actors/README.md @@ -0,0 +1,125 @@ +# LINGYUN.Abp.Dapr.Actors + +Abp Dapr.Actors 集成 + +## 配置使用 + +项目设计与 **Volo.Abp.Http.Client** 类似 + +### 1、接口定义 + +```c# + +// 定义在接口上的RemoteService.Name会被作为Actor的名称注册到Dapr +[RemoteService(Name = "System")] +public interface ISystemActor : IActor +{ + Task GetAsync(); +} + +public class SystemActorInterfaceModule : AbpModule +{ + +} + +``` + +### 2、服务端 + +引用 LINGYUN.Abp.Dapr.Actors.AspNetCore + +* 实现接口 + +```c# + +public class SystemActor : Actor +{ + public Task GetAsync() + { + retuen Task.CompletedTask; + } +} + +``` + +* 创建模块 + +```c# + +// 模块会自动搜索实现了IActor的服务,注册为Dapr的Actors +[DependsOn( + typeof(AbpDaprActorsAspNetCoreModule))] +public class SystemActorServerModule : AbpModule +{ + +} + +``` + +* 发布到Dapr + +```shell +# --app-port .net程序映射端口 +# -H 对外暴露 http 监听端口 +# -G 对外暴露 grpc 监听端口 +dapr run --app-id ufsoft --app-port 5000 -H 50000 -G 40001 -- dotnet run + +``` + +### 3、客户端 + +引用 LINGYUN.Abp.Dapr.Actors + +* 配置文件 **appsettings.json** + +```json + +{ + "RemoteActors": { + "System": { + "ActorId": "1", + "BaseUrl": "http://127.0.0.1:50000" + } + } +} + +``` + +* 客户端代码 + +```c# + +// 模块依赖 +[DependsOn( + typeof(AbpDaprActorsModule))] +public class SystemActorClientModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + // 注册代理类似于 Volo.Abp.Http.Client 模块 + context.Services.AddDaprActorProxies( + typeof(SystemActorInterfaceModule).Assembly, // 搜索 SystemActorInterfaceModule 模块下的IActor定义 + RemoteServiceName + ); + } +} + +// 调用方法,直接依赖注入即可 +public class InvokeClass +{ + private readonly ISystemActor _systemActor; + + public InvokeClass(ISystemActor systemActor) + { + _systemActor = systemActor; + } + + public async Task InvokeAsync() + { + await _systemActor.GetAsync(); + } +} + +``` + +## 其他