committed by
GitHub
32 changed files with 1194 additions and 0 deletions
@ -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<ActorRegistration> registrations, |
|||
Type implementationType) |
|||
{ |
|||
return registrations.Any(x => x.Type.ImplementationType == implementationType); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net5.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Dapr.Actors.AspNetCore" Version="1.1.0" /> |
|||
<PackageReference Include="Volo.Abp.AspNetCore" Version="4.2.1" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -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<AbpEndpointRouterOptions>(options => |
|||
{ |
|||
options.EndpointConfigureActions.Add(endpointContext => |
|||
{ |
|||
endpointContext.Endpoints.MapActorsHandlers(); |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
private static void AddDefinitionActor(IServiceCollection services) |
|||
{ |
|||
var actorRegistrations = new List<ActorRegistration>(); |
|||
|
|||
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); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -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 |
|||
} |
|||
``` |
|||
## 配置项说明 |
|||
|
|||
|
|||
## 其他 |
|||
@ -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<Type> list = new List<Type>(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<RemoteServiceAttribute>()) |
|||
.FirstOrDefault(); |
|||
} |
|||
|
|||
public static Type GetNonActorParentType(this Type type) |
|||
{ |
|||
List<Type> list = new List<Type>(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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk.Web"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net5.0</TargetFramework> |
|||
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<IsPackable>true</IsPackable> |
|||
<OutputType>Library</OutputType> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.Dapr.Actors.IdentityModel\LINGYUN.Abp.Dapr.Actors.IdentityModel.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,6 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<PropertyGroup> |
|||
<ActiveDebugProfile>IIS Express</ActiveDebugProfile> |
|||
</PropertyGroup> |
|||
</Project> |
|||
@ -0,0 +1,10 @@ |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Dapr.Actors.IdentityModel.Web |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpDaprActorsIdentityModelModule))] |
|||
public class AbpDaprActorsIdentityModelWebModule : AbpModule |
|||
{ |
|||
} |
|||
} |
|||
@ -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<string> GetAccessTokenFromHttpContextOrNullAsync() |
|||
{ |
|||
var httpContext = HttpContextAccessor?.HttpContext; |
|||
if (httpContext == null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return await httpContext.GetTokenAsync("access_token"); |
|||
} |
|||
} |
|||
} |
|||
@ -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" |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
# LINGYUN.Abp.Dapr.Actors.IdentityModel.Web |
|||
|
|||
Dapr.Actors内部使用Http进行服务间调用,此模块用于获取应用当前状态中的身份令牌并传递到远程Actor服务 |
|||
|
|||
## 配置使用 |
|||
|
|||
模块按需引用 |
|||
|
|||
```csharp |
|||
[DependsOn(typeof(AbpDaprActorsIdentityModelWebModule))] |
|||
public class YouProjectModule : AbpModule |
|||
{ |
|||
// other |
|||
} |
|||
``` |
|||
## 配置项说明 |
|||
|
|||
|
|||
## 其他 |
|||
@ -0,0 +1,18 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net5.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.IdentityModel" Version="4.2.1" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.Dapr.Actors\LINGYUN.Abp.Dapr.Actors.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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 |
|||
{ |
|||
} |
|||
} |
|||
@ -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<IdentityModelDaprActorProxyAuthenticator> Logger { get; set; } |
|||
|
|||
public IdentityModelDaprActorProxyAuthenticator( |
|||
IIdentityModelAuthenticationService identityModelAuthenticationService) |
|||
{ |
|||
IdentityModelAuthenticationService = identityModelAuthenticationService; |
|||
Logger = NullLogger<IdentityModelDaprActorProxyAuthenticator>.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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
# LINGYUN.Abp.Dapr.Actors.IdentityModel |
|||
|
|||
Dapr.Actors内部使用Http进行服务间调用,此模块用于传递服务间调用令牌 |
|||
|
|||
## 配置使用 |
|||
|
|||
模块按需引用 |
|||
|
|||
```csharp |
|||
[DependsOn(typeof(AbpDaprActorsIdentityModelModule))] |
|||
public class YouProjectModule : AbpModule |
|||
{ |
|||
// other |
|||
} |
|||
``` |
|||
## 配置项说明 |
|||
|
|||
|
|||
## 其他 |
|||
@ -0,0 +1,17 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net5.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Dapr.Actors" Version="1.1.0" /> |
|||
<PackageReference Include="Volo.Abp.Castle.Core" Version="4.2.1" /> |
|||
<PackageReference Include="Volo.Abp.ExceptionHandling" Version="4.2.1" /> |
|||
<PackageReference Include="Volo.Abp.MultiTenancy" Version="4.2.1" /> |
|||
<PackageReference Include="Volo.Abp.Validation" Version="4.2.1" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
@ -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]; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
namespace LINGYUN.Abp.Dapr.Actors |
|||
{ |
|||
public class AbpDaprActorOptions |
|||
{ |
|||
public DaprActorConfigurationDictionary RemoteActors { get; set; } |
|||
|
|||
public AbpDaprActorOptions() |
|||
{ |
|||
RemoteActors = new DaprActorConfigurationDictionary(); |
|||
} |
|||
} |
|||
} |
|||
@ -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<Type, DynamicDaprActorProxyConfig> ActorProxies { get; set; } |
|||
|
|||
public AbpDaprActorProxyOptions() |
|||
{ |
|||
ActorProxies = new Dictionary<Type, DynamicDaprActorProxyConfig>(); |
|||
} |
|||
} |
|||
} |
|||
@ -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<AbpDaprActorOptions>(configuration); |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.Dapr.Actors.Authentication |
|||
{ |
|||
public interface IDaprActorProxyAuthenticator |
|||
{ |
|||
Task AuthenticateAsync(DaprActorProxyAuthenticateContext context); |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.Dapr.Actors |
|||
{ |
|||
public class DaprActorConfiguration : Dictionary<string, string> |
|||
{ |
|||
/// <summary>
|
|||
/// Base ActorId.
|
|||
/// </summary>
|
|||
public string ActorId |
|||
{ |
|||
get => this.GetOrDefault(nameof(ActorId)); |
|||
set => this[nameof(ActorId)] = value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Base Url.
|
|||
/// </summary>
|
|||
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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
using System.Collections.Generic; |
|||
using Volo.Abp; |
|||
|
|||
namespace LINGYUN.Abp.Dapr.Actors |
|||
{ |
|||
public class DaprActorConfigurationDictionary : Dictionary<string, DaprActorConfiguration> |
|||
{ |
|||
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."); |
|||
} |
|||
} |
|||
} |
|||
@ -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<HttpRequestMessage> _preConfigureInvoke; |
|||
protected Action<HttpRequestMessage> PreConfigureInvoke => _preConfigureInvoke; |
|||
|
|||
public virtual void PreConfigure(Action<HttpRequestMessage> 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<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) |
|||
{ |
|||
PreConfigureInvoke?.Invoke(request); |
|||
|
|||
return await base.SendAsync(request, cancellationToken); |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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<TService> : AbpInterceptor, ITransientDependency |
|||
where TService: IActor |
|||
{ |
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
protected AbpDaprActorOptions DaprActorOptions { get; } |
|||
protected AbpDaprActorProxyOptions DaprActorProxyOptions { get; } |
|||
protected IDaprActorProxyAuthenticator ActoryProxyAuthenticator { get; } |
|||
|
|||
public ILogger<DynamicDaprActorProxyInterceptor<TService>> Logger { get; set; } |
|||
|
|||
public DynamicDaprActorProxyInterceptor( |
|||
IOptions<AbpDaprActorProxyOptions> daprActorProxyOptions, |
|||
IOptionsSnapshot<AbpDaprActorOptions> daprActorOptions, |
|||
IDaprActorProxyAuthenticator actorProxyAuthenticator, |
|||
ICurrentTenant currentTenant) |
|||
{ |
|||
CurrentTenant = currentTenant; |
|||
ActoryProxyAuthenticator = actorProxyAuthenticator; |
|||
DaprActorProxyOptions = daprActorProxyOptions.Value; |
|||
DaprActorOptions = daprActorOptions.Value; |
|||
|
|||
Logger = NullLogger<DynamicDaprActorProxyInterceptor<TService>>.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<RemoteServiceAttribute>(); |
|||
var actorType = remoteServiceAttr != null |
|||
? remoteServiceAttr.Name |
|||
: invokeType.Name; |
|||
var isAsyncMethod = invocation.Method.IsAsync(); |
|||
|
|||
try |
|||
{ |
|||
var actorProxy = proxyFactory.CreateActorProxy<TService>(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<object>.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))); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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<T>( |
|||
[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<AbpDaprActorProxyOptions>(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); |
|||
} |
|||
} |
|||
} |
|||
@ -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<string> GetAsync(); |
|||
} |
|||
|
|||
public class SystemActorInterfaceModule : AbpModule |
|||
{ |
|||
|
|||
} |
|||
|
|||
``` |
|||
|
|||
### 2、服务端 |
|||
|
|||
引用 LINGYUN.Abp.Dapr.Actors.AspNetCore |
|||
|
|||
* 实现接口 |
|||
|
|||
```c# |
|||
|
|||
public class SystemActor : Actor |
|||
{ |
|||
public Task<string> 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(); |
|||
} |
|||
} |
|||
|
|||
``` |
|||
|
|||
## 其他 |
|||
Loading…
Reference in new issue