committed by
GitHub
22 changed files with 1094 additions and 21 deletions
@ -0,0 +1,13 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>net5.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Dapr.Client" Version="1.1.0" /> |
||||
|
<PackageReference Include="Volo.Abp.Http.Client" Version="4.2.1" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,21 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Volo.Abp.Http.Client; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Dapr.Client |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpHttpClientModule))] |
||||
|
public class AbpDaprClientModule : AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
var configuration = context.Services.GetConfiguration(); |
||||
|
Configure<AbpDaprRemoteServiceOptions>(configuration); |
||||
|
Configure<AbpDaprClientOptions>(configuration.GetSection("Dapr:Client")); |
||||
|
|
||||
|
// DaprClient应该配置为单例的实现
|
||||
|
context.Services.AddDaprClient(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
using Grpc.Net.Client; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Dapr.Client |
||||
|
{ |
||||
|
public class AbpDaprClientOptions |
||||
|
{ |
||||
|
public string GrpcEndpoint { get; set; } |
||||
|
public string HttpEndpoint { get; set; } |
||||
|
public GrpcChannelOptions GrpcChannelOptions { get; set; } |
||||
|
public AbpDaprClientOptions() |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
namespace LINGYUN.Abp.Dapr.Client |
||||
|
{ |
||||
|
public class AbpDaprRemoteServiceOptions |
||||
|
{ |
||||
|
public DaprRemoteServiceConfigurationDictionary RemoteServices { get; set; } |
||||
|
|
||||
|
public AbpDaprRemoteServiceOptions() |
||||
|
{ |
||||
|
RemoteServices = new DaprRemoteServiceConfigurationDictionary(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Dapr.Client.Authentication |
||||
|
{ |
||||
|
public interface IRemoteServiceDaprClientAuthenticator |
||||
|
{ |
||||
|
Task AuthenticateAsync(RemoteServiceDaprClientAuthenticateContext context); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Dapr.Client.Authentication |
||||
|
{ |
||||
|
[Dependency(TryRegister = true)] |
||||
|
public class NullRemoteServiceDaprClientAuthenticator : IRemoteServiceDaprClientAuthenticator, ISingletonDependency |
||||
|
{ |
||||
|
public Task AuthenticateAsync(RemoteServiceDaprClientAuthenticateContext context) |
||||
|
{ |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
using System.Net.Http; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Dapr.Client.Authentication |
||||
|
{ |
||||
|
public class RemoteServiceDaprClientAuthenticateContext |
||||
|
{ |
||||
|
public HttpRequestMessage Request { get; } |
||||
|
public DaprRemoteServiceConfiguration RemoteService { get; } |
||||
|
|
||||
|
public string RemoteServiceName { get; } |
||||
|
public RemoteServiceDaprClientAuthenticateContext( |
||||
|
HttpRequestMessage request, |
||||
|
DaprRemoteServiceConfiguration remoteService, |
||||
|
string remoteServiceName) |
||||
|
{ |
||||
|
Request = request; |
||||
|
RemoteService = remoteService; |
||||
|
RemoteServiceName = remoteServiceName; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,45 @@ |
|||||
|
using Dapr.Client; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using System; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Json.SystemTextJson; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Dapr.Client |
||||
|
{ |
||||
|
public class DaprClientFactory : IDaprClientFactory, ISingletonDependency |
||||
|
{ |
||||
|
protected AbpDaprClientOptions DaprClientOptions { get; } |
||||
|
protected AbpSystemTextJsonSerializerOptions JsonSerializerOptions { get; } |
||||
|
|
||||
|
private readonly Lazy<DaprClient> _daprClientLazy; |
||||
|
|
||||
|
public DaprClientFactory( |
||||
|
IOptions<AbpDaprClientOptions> daprClientOptions, |
||||
|
IOptions<AbpSystemTextJsonSerializerOptions> jsonSerializarOptions) |
||||
|
{ |
||||
|
DaprClientOptions = daprClientOptions.Value; |
||||
|
JsonSerializerOptions = jsonSerializarOptions.Value; |
||||
|
|
||||
|
_daprClientLazy = new Lazy<DaprClient>(() => CreateDaprClient()); |
||||
|
} |
||||
|
|
||||
|
public DaprClient Create() => _daprClientLazy.Value; |
||||
|
|
||||
|
protected virtual DaprClient CreateDaprClient() |
||||
|
{ |
||||
|
var builder = new DaprClientBuilder() |
||||
|
.UseHttpEndpoint(DaprClientOptions.HttpEndpoint) |
||||
|
.UseJsonSerializationOptions(JsonSerializerOptions.JsonSerializerOptions); |
||||
|
|
||||
|
if (!DaprClientOptions.GrpcEndpoint.IsNullOrWhiteSpace() && |
||||
|
DaprClientOptions.GrpcChannelOptions != null) |
||||
|
{ |
||||
|
builder |
||||
|
.UseGrpcEndpoint(DaprClientOptions.GrpcEndpoint) |
||||
|
.UseGrpcChannelOptions(DaprClientOptions.GrpcChannelOptions); |
||||
|
} |
||||
|
|
||||
|
return builder.Build(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Dapr.Client |
||||
|
{ |
||||
|
public class DaprRemoteServiceConfiguration : Dictionary<string, string> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Base AppId.
|
||||
|
/// </summary>
|
||||
|
public string AppId |
||||
|
{ |
||||
|
get => this.GetOrDefault(nameof(AppId)); |
||||
|
set => this[nameof(AppId)] = value; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Version.
|
||||
|
/// </summary>
|
||||
|
public string Version |
||||
|
{ |
||||
|
get => this.GetOrDefault(nameof(Version)); |
||||
|
set => this[nameof(Version)] = value; |
||||
|
} |
||||
|
|
||||
|
public DaprRemoteServiceConfiguration() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public DaprRemoteServiceConfiguration( |
||||
|
string appId, |
||||
|
string version) |
||||
|
{ |
||||
|
this[nameof(AppId)] = appId; |
||||
|
this[nameof(Version)] = version; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Volo.Abp; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Dapr.Client |
||||
|
{ |
||||
|
public class DaprRemoteServiceConfigurationDictionary : Dictionary<string, DaprRemoteServiceConfiguration> |
||||
|
{ |
||||
|
public const string DefaultName = "Default"; |
||||
|
|
||||
|
public DaprRemoteServiceConfiguration Default |
||||
|
{ |
||||
|
get => this.GetOrDefault(DefaultName); |
||||
|
set => this[DefaultName] = value; |
||||
|
} |
||||
|
|
||||
|
public DaprRemoteServiceConfiguration GetConfigurationOrDefault(string name) |
||||
|
{ |
||||
|
return this.GetOrDefault(name) |
||||
|
?? Default |
||||
|
?? throw new AbpException($"Dapr remote service '{name}' was not found and there is no default configuration."); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Dapr.Client.DynamicProxying |
||||
|
{ |
||||
|
public class AbpDaprClientProxyOptions |
||||
|
{ |
||||
|
public Dictionary<Type, DynamicDaprClientProxyConfig> DaprClientProxies { get; set; } |
||||
|
|
||||
|
public AbpDaprClientProxyOptions() |
||||
|
{ |
||||
|
DaprClientProxies = new Dictionary<Type, DynamicDaprClientProxyConfig>(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,160 @@ |
|||||
|
using Dapr.Client; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Globalization; |
||||
|
using System.Linq; |
||||
|
using System.Net.Http; |
||||
|
using System.Net.Http.Headers; |
||||
|
using System.Reflection; |
||||
|
using System.Text.Json; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Http.Client.DynamicProxying; |
||||
|
using Volo.Abp.Http.Modeling; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
using Volo.Abp.Threading; |
||||
|
using Volo.Abp.Tracing; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Dapr.Client.DynamicProxying |
||||
|
{ |
||||
|
public class DaprApiDescriptionFinder : IDaprApiDescriptionFinder, ITransientDependency |
||||
|
{ |
||||
|
public ICancellationTokenProvider CancellationTokenProvider { get; set; } |
||||
|
protected IApiDescriptionCache Cache { get; } |
||||
|
protected AbpCorrelationIdOptions AbpCorrelationIdOptions { get; } |
||||
|
protected ICorrelationIdProvider CorrelationIdProvider { get; } |
||||
|
protected ICurrentTenant CurrentTenant { get; } |
||||
|
|
||||
|
protected DaprClient DaprClient { get; } |
||||
|
public DaprApiDescriptionFinder( |
||||
|
DaprClient daprClient, |
||||
|
IApiDescriptionCache cache, |
||||
|
IOptions<AbpCorrelationIdOptions> abpCorrelationIdOptions, |
||||
|
ICorrelationIdProvider correlationIdProvider, |
||||
|
ICurrentTenant currentTenant) |
||||
|
{ |
||||
|
DaprClient = daprClient; |
||||
|
|
||||
|
Cache = cache; |
||||
|
AbpCorrelationIdOptions = abpCorrelationIdOptions.Value; |
||||
|
CorrelationIdProvider = correlationIdProvider; |
||||
|
CurrentTenant = currentTenant; |
||||
|
CancellationTokenProvider = NullCancellationTokenProvider.Instance; |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<ActionApiDescriptionModel> FindActionAsync(string appId, Type serviceType, MethodInfo method) |
||||
|
{ |
||||
|
var apiDescription = await GetApiDescriptionAsync(appId); |
||||
|
|
||||
|
//TODO: Cache finding?
|
||||
|
|
||||
|
var methodParameters = method.GetParameters().ToArray(); |
||||
|
|
||||
|
foreach (var module in apiDescription.Modules.Values) |
||||
|
{ |
||||
|
foreach (var controller in module.Controllers.Values) |
||||
|
{ |
||||
|
if (!controller.Implements(serviceType)) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
foreach (var action in controller.Actions.Values) |
||||
|
{ |
||||
|
if (action.Name == method.Name && action.ParametersOnMethod.Count == methodParameters.Length) |
||||
|
{ |
||||
|
var found = true; |
||||
|
|
||||
|
for (int i = 0; i < methodParameters.Length; i++) |
||||
|
{ |
||||
|
if (!TypeMatches(action.ParametersOnMethod[i], methodParameters[i])) |
||||
|
{ |
||||
|
found = false; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (found) |
||||
|
{ |
||||
|
return action; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
throw new AbpException($"Could not found remote action for method: {method} on the appId: {appId}"); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<ApplicationApiDescriptionModel> GetApiDescriptionAsync(string appId) |
||||
|
{ |
||||
|
return await Cache.GetAsync(appId, () => GetApiDescriptionFromServerAsync(appId)); |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task<ApplicationApiDescriptionModel> GetApiDescriptionFromServerAsync(string appId) |
||||
|
{ |
||||
|
var requestMessage = DaprClient.CreateInvokeMethodRequest(HttpMethod.Get, appId, "api/abp/api-definition"); |
||||
|
|
||||
|
AddHeaders(requestMessage); |
||||
|
|
||||
|
var response = await DaprClient.InvokeMethodWithResponseAsync( |
||||
|
requestMessage, |
||||
|
CancellationTokenProvider.Token); |
||||
|
|
||||
|
if (!response.IsSuccessStatusCode) |
||||
|
{ |
||||
|
throw new AbpException("Remote service returns error! StatusCode = " + response.StatusCode); |
||||
|
} |
||||
|
|
||||
|
var content = await response.Content.ReadAsStringAsync(); |
||||
|
|
||||
|
var result = JsonSerializer.Deserialize<ApplicationApiDescriptionModel>(content, new JsonSerializerOptions |
||||
|
{ |
||||
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase |
||||
|
}); |
||||
|
|
||||
|
return (ApplicationApiDescriptionModel)result; |
||||
|
} |
||||
|
|
||||
|
protected virtual void AddHeaders(HttpRequestMessage requestMessage) |
||||
|
{ |
||||
|
//CorrelationId
|
||||
|
requestMessage.Headers.Add(AbpCorrelationIdOptions.HttpHeaderName, CorrelationIdProvider.Get()); |
||||
|
|
||||
|
//TenantId
|
||||
|
if (CurrentTenant.Id.HasValue) |
||||
|
{ |
||||
|
//TODO: Use AbpAspNetCoreMultiTenancyOptions to get the key
|
||||
|
requestMessage.Headers.Add(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()) |
||||
|
{ |
||||
|
requestMessage.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue(currentCulture)); |
||||
|
} |
||||
|
|
||||
|
//X-Requested-With
|
||||
|
requestMessage.Headers.Add("X-Requested-With", "XMLHttpRequest"); |
||||
|
} |
||||
|
|
||||
|
protected virtual bool TypeMatches(MethodParameterApiDescriptionModel actionParameter, ParameterInfo methodParameter) |
||||
|
{ |
||||
|
return NormalizeTypeName(actionParameter.TypeAsString) == |
||||
|
NormalizeTypeName(methodParameter.ParameterType.GetFullNameWithAssemblyName()); |
||||
|
} |
||||
|
|
||||
|
protected virtual string NormalizeTypeName(string typeName) |
||||
|
{ |
||||
|
const string placeholder = "%COREFX%"; |
||||
|
const string netCoreLib = "System.Private.CoreLib"; |
||||
|
const string netFxLib = "mscorlib"; |
||||
|
|
||||
|
return typeName.Replace(netCoreLib, placeholder).Replace(netFxLib, placeholder); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Dapr.Client.DynamicProxying |
||||
|
{ |
||||
|
public class DynamicDaprClientProxyConfig |
||||
|
{ |
||||
|
public Type Type { get; } |
||||
|
|
||||
|
public string RemoteServiceName { get; } |
||||
|
|
||||
|
public DynamicDaprClientProxyConfig(Type type, string remoteServiceName) |
||||
|
{ |
||||
|
Type = type; |
||||
|
RemoteServiceName = remoteServiceName; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,299 @@ |
|||||
|
using Dapr.Client; |
||||
|
using LINGYUN.Abp.Dapr.Client.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.Linq; |
||||
|
using System.Net.Http; |
||||
|
using System.Net.Http.Headers; |
||||
|
using System.Reflection; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.Content; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.DynamicProxy; |
||||
|
using Volo.Abp.Http; |
||||
|
using Volo.Abp.Http.Client; |
||||
|
using Volo.Abp.Http.Client.DynamicProxying; |
||||
|
using Volo.Abp.Http.Modeling; |
||||
|
using Volo.Abp.Http.ProxyScripting.Generators; |
||||
|
using Volo.Abp.Json; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
using Volo.Abp.Threading; |
||||
|
using Volo.Abp.Tracing; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Dapr.Client.DynamicProxying |
||||
|
{ |
||||
|
public class DynamicDaprClientProxyInterceptor<TService> : AbpInterceptor, ITransientDependency |
||||
|
{ |
||||
|
protected static MethodInfo MakeRequestAndGetResultAsyncMethod { get; } |
||||
|
|
||||
|
protected DaprClient DaprClient { get; } |
||||
|
protected ICancellationTokenProvider CancellationTokenProvider { get; } |
||||
|
protected ICorrelationIdProvider CorrelationIdProvider { get; } |
||||
|
protected ICurrentTenant CurrentTenant { get; } |
||||
|
protected AbpCorrelationIdOptions AbpCorrelationIdOptions { get; } |
||||
|
protected IDaprApiDescriptionFinder ApiDescriptionFinder { get; } |
||||
|
protected AbpDaprRemoteServiceOptions AbpRemoteServiceOptions { get; } |
||||
|
protected AbpDaprClientProxyOptions ClientProxyOptions { get; } |
||||
|
protected IJsonSerializer JsonSerializer { get; } |
||||
|
protected IRemoteServiceDaprClientAuthenticator ClientAuthenticator { get; } |
||||
|
|
||||
|
public ILogger<DynamicDaprClientProxyInterceptor<TService>> Logger { get; set; } |
||||
|
|
||||
|
static DynamicDaprClientProxyInterceptor() |
||||
|
{ |
||||
|
MakeRequestAndGetResultAsyncMethod = typeof(DynamicDaprClientProxyInterceptor<TService>) |
||||
|
.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance) |
||||
|
.First(m => m.Name == nameof(MakeRequestAndGetResultAsync) && m.IsGenericMethodDefinition); |
||||
|
} |
||||
|
|
||||
|
public DynamicDaprClientProxyInterceptor( |
||||
|
DaprClient daprClient, |
||||
|
IOptions<AbpDaprClientProxyOptions> clientProxyOptions, |
||||
|
IOptionsSnapshot<AbpDaprRemoteServiceOptions> remoteServiceOptions, |
||||
|
IDaprApiDescriptionFinder apiDescriptionFinder, |
||||
|
IJsonSerializer jsonSerializer, |
||||
|
IRemoteServiceDaprClientAuthenticator clientAuthenticator, |
||||
|
ICancellationTokenProvider cancellationTokenProvider, |
||||
|
ICorrelationIdProvider correlationIdProvider, |
||||
|
IOptions<AbpCorrelationIdOptions> correlationIdOptions, |
||||
|
ICurrentTenant currentTenant) |
||||
|
{ |
||||
|
DaprClient = daprClient; |
||||
|
CancellationTokenProvider = cancellationTokenProvider; |
||||
|
CorrelationIdProvider = correlationIdProvider; |
||||
|
CurrentTenant = currentTenant; |
||||
|
AbpCorrelationIdOptions = correlationIdOptions.Value; |
||||
|
ApiDescriptionFinder = apiDescriptionFinder; |
||||
|
JsonSerializer = jsonSerializer; |
||||
|
ClientAuthenticator = clientAuthenticator; |
||||
|
ClientProxyOptions = clientProxyOptions.Value; |
||||
|
AbpRemoteServiceOptions = remoteServiceOptions.Value; |
||||
|
|
||||
|
Logger = NullLogger<DynamicDaprClientProxyInterceptor<TService>>.Instance; |
||||
|
} |
||||
|
|
||||
|
public override async Task InterceptAsync(IAbpMethodInvocation invocation) |
||||
|
{ |
||||
|
if (invocation.Method.ReturnType.GenericTypeArguments.IsNullOrEmpty()) |
||||
|
{ |
||||
|
await MakeRequestAsync(invocation); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
var result = (Task)MakeRequestAndGetResultAsyncMethod |
||||
|
.MakeGenericMethod(invocation.Method.ReturnType.GenericTypeArguments[0]) |
||||
|
.Invoke(this, new object[] { invocation }); |
||||
|
|
||||
|
invocation.ReturnValue = await GetResultAsync( |
||||
|
result, |
||||
|
invocation.Method.ReturnType.GetGenericArguments()[0] |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private async Task<object> GetResultAsync(Task task, Type resultType) |
||||
|
{ |
||||
|
await task; |
||||
|
return typeof(Task<>) |
||||
|
.MakeGenericType(resultType) |
||||
|
.GetProperty(nameof(Task<object>.Result), BindingFlags.Instance | BindingFlags.Public) |
||||
|
.GetValue(task); |
||||
|
} |
||||
|
|
||||
|
private async Task<T> MakeRequestAndGetResultAsync<T>(IAbpMethodInvocation invocation) |
||||
|
{ |
||||
|
var responseContent = await MakeRequestAsync(invocation); |
||||
|
|
||||
|
if (typeof(T) == typeof(IRemoteStreamContent)) |
||||
|
{ |
||||
|
/* returning a class that holds a reference to response |
||||
|
* content just to be sure that GC does not dispose of |
||||
|
* it before we finish doing our work with the stream */ |
||||
|
return (T)(object)new RemoteStreamContent(await responseContent.ReadAsStreamAsync()) |
||||
|
{ |
||||
|
ContentType = responseContent.Headers.ContentType?.ToString() |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
var stringContent = await responseContent.ReadAsStringAsync(); |
||||
|
if (typeof(T) == typeof(string)) |
||||
|
{ |
||||
|
return (T)(object)stringContent; |
||||
|
} |
||||
|
|
||||
|
if (stringContent.IsNullOrWhiteSpace()) |
||||
|
{ |
||||
|
return default; |
||||
|
} |
||||
|
|
||||
|
return JsonSerializer.Deserialize<T>(stringContent); |
||||
|
} |
||||
|
|
||||
|
private async Task<HttpContent> MakeRequestAsync(IAbpMethodInvocation invocation) |
||||
|
{ |
||||
|
var clientConfig = ClientProxyOptions.DaprClientProxies.GetOrDefault(typeof(TService)) ?? throw new AbpException($"Could not get DynamicDaprClientProxyConfig for {typeof(TService).FullName}."); |
||||
|
var remoteServiceConfig = AbpRemoteServiceOptions.RemoteServices.GetConfigurationOrDefault(clientConfig.RemoteServiceName); |
||||
|
|
||||
|
// 遵循远端 api/abp/api-definition
|
||||
|
var action = await ApiDescriptionFinder.FindActionAsync( |
||||
|
remoteServiceConfig.AppId, |
||||
|
typeof(TService), |
||||
|
invocation.Method |
||||
|
); |
||||
|
|
||||
|
var apiVersion = GetApiVersionInfo(action); |
||||
|
|
||||
|
// See: https://docs.dapr.io/reference/api/service_invocation_api/#examples
|
||||
|
// 需要合并端点作为dapr远程调用的方法名称
|
||||
|
var methodName = UrlBuilder.GenerateUrlWithParameters(action, invocation.ArgumentsDictionary, apiVersion); |
||||
|
|
||||
|
var requestMessage = DaprClient.CreateInvokeMethodRequest( |
||||
|
action.GetHttpMethod(), |
||||
|
remoteServiceConfig.AppId, |
||||
|
methodName); |
||||
|
|
||||
|
requestMessage.Content = RequestPayloadBuilder.BuildContent(action, invocation.ArgumentsDictionary, JsonSerializer, apiVersion); |
||||
|
|
||||
|
AddHeaders(invocation, action, requestMessage, apiVersion); |
||||
|
|
||||
|
await ClientAuthenticator.AuthenticateAsync( |
||||
|
new RemoteServiceDaprClientAuthenticateContext( |
||||
|
requestMessage, |
||||
|
remoteServiceConfig, |
||||
|
clientConfig.RemoteServiceName |
||||
|
) |
||||
|
); |
||||
|
|
||||
|
var response = await DaprClient.InvokeMethodWithResponseAsync(requestMessage, GetCancellationToken()); |
||||
|
|
||||
|
if (!response.IsSuccessStatusCode) |
||||
|
{ |
||||
|
await ThrowExceptionForResponseAsync(response); |
||||
|
} |
||||
|
|
||||
|
return response.Content; |
||||
|
} |
||||
|
|
||||
|
private ApiVersionInfo GetApiVersionInfo(ActionApiDescriptionModel action) |
||||
|
{ |
||||
|
var apiVersion = FindBestApiVersion(action); |
||||
|
|
||||
|
//TODO: Make names configurable?
|
||||
|
var versionParam = action.Parameters.FirstOrDefault(p => p.Name == "apiVersion" && p.BindingSourceId == ParameterBindingSources.Path) ?? |
||||
|
action.Parameters.FirstOrDefault(p => p.Name == "api-version" && p.BindingSourceId == ParameterBindingSources.Query); |
||||
|
|
||||
|
return new ApiVersionInfo(versionParam?.BindingSourceId, apiVersion); |
||||
|
} |
||||
|
|
||||
|
private string FindBestApiVersion(ActionApiDescriptionModel action) |
||||
|
{ |
||||
|
var configuredVersion = GetConfiguredApiVersion(); |
||||
|
|
||||
|
if (action.SupportedVersions.IsNullOrEmpty()) |
||||
|
{ |
||||
|
return configuredVersion ?? "1.0"; |
||||
|
} |
||||
|
|
||||
|
if (action.SupportedVersions.Contains(configuredVersion)) |
||||
|
{ |
||||
|
return configuredVersion; |
||||
|
} |
||||
|
|
||||
|
return action.SupportedVersions.Last(); //TODO: Ensure to get the latest version!
|
||||
|
} |
||||
|
|
||||
|
protected virtual void AddHeaders( |
||||
|
IAbpMethodInvocation invocation, |
||||
|
ActionApiDescriptionModel action, |
||||
|
HttpRequestMessage requestMessage, |
||||
|
ApiVersionInfo apiVersion) |
||||
|
{ |
||||
|
//API Version
|
||||
|
if (!apiVersion.Version.IsNullOrEmpty()) |
||||
|
{ |
||||
|
//TODO: What about other media types?
|
||||
|
requestMessage.Headers.Add("accept", $"{MimeTypes.Text.Plain}; v={apiVersion.Version}"); |
||||
|
requestMessage.Headers.Add("accept", $"{MimeTypes.Application.Json}; v={apiVersion.Version}"); |
||||
|
requestMessage.Headers.Add("api-version", apiVersion.Version); |
||||
|
} |
||||
|
|
||||
|
//Header parameters
|
||||
|
var headers = action.Parameters.Where(p => p.BindingSourceId == ParameterBindingSources.Header).ToArray(); |
||||
|
foreach (var headerParameter in headers) |
||||
|
{ |
||||
|
var value = HttpActionParameterHelper.FindParameterValue(invocation.ArgumentsDictionary, headerParameter); |
||||
|
if (value != null) |
||||
|
{ |
||||
|
requestMessage.Headers.Add(headerParameter.Name, value.ToString()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//CorrelationId
|
||||
|
requestMessage.Headers.Add(AbpCorrelationIdOptions.HttpHeaderName, CorrelationIdProvider.Get()); |
||||
|
|
||||
|
//TenantId
|
||||
|
if (CurrentTenant.Id.HasValue) |
||||
|
{ |
||||
|
//TODO: Use AbpAspNetCoreMultiTenancyOptions to get the key
|
||||
|
requestMessage.Headers.Add(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()) |
||||
|
{ |
||||
|
requestMessage.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue(currentCulture)); |
||||
|
} |
||||
|
|
||||
|
//X-Requested-With
|
||||
|
requestMessage.Headers.Add("X-Requested-With", "XMLHttpRequest"); |
||||
|
} |
||||
|
|
||||
|
private string GetConfiguredApiVersion() |
||||
|
{ |
||||
|
var clientConfig = ClientProxyOptions.DaprClientProxies.GetOrDefault(typeof(TService)) |
||||
|
?? throw new AbpException($"Could not get DynamicDaprClientProxyConfig for {typeof(TService).FullName}."); |
||||
|
|
||||
|
return AbpRemoteServiceOptions.RemoteServices.GetOrDefault(clientConfig.RemoteServiceName)?.Version |
||||
|
?? AbpRemoteServiceOptions.RemoteServices.Default?.Version; |
||||
|
} |
||||
|
|
||||
|
private async Task ThrowExceptionForResponseAsync(HttpResponseMessage response) |
||||
|
{ |
||||
|
if (response.Headers.Contains(AbpHttpConsts.AbpErrorFormat)) |
||||
|
{ |
||||
|
var errorResponse = JsonSerializer.Deserialize<RemoteServiceErrorResponse>( |
||||
|
await response.Content.ReadAsStringAsync() |
||||
|
); |
||||
|
|
||||
|
throw new AbpRemoteCallException(errorResponse.Error) |
||||
|
{ |
||||
|
HttpStatusCode = (int)response.StatusCode |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
throw new AbpRemoteCallException( |
||||
|
new RemoteServiceErrorInfo |
||||
|
{ |
||||
|
Message = response.ReasonPhrase, |
||||
|
Code = response.StatusCode.ToString() |
||||
|
} |
||||
|
) |
||||
|
{ |
||||
|
HttpStatusCode = (int)response.StatusCode |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
protected virtual CancellationToken GetCancellationToken() |
||||
|
{ |
||||
|
return CancellationTokenProvider.Token; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Volo.Abp.Http.Modeling; |
||||
|
using Volo.Abp.Reflection; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Dapr.Client.DynamicProxying |
||||
|
{ |
||||
|
internal static class HttpActionParameterHelper |
||||
|
{ |
||||
|
public static object FindParameterValue(IReadOnlyDictionary<string, object> methodArguments, ParameterApiDescriptionModel apiParameter) |
||||
|
{ |
||||
|
var value = methodArguments.GetOrDefault(apiParameter.NameOnMethod); |
||||
|
if (value == null) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
if (apiParameter.Name == apiParameter.NameOnMethod) |
||||
|
{ |
||||
|
return value; |
||||
|
} |
||||
|
|
||||
|
return ReflectionHelper.GetValueByPath(value, value.GetType(), apiParameter.Name); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
using System; |
||||
|
using System.Reflection; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Http.Modeling; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Dapr.Client.DynamicProxying |
||||
|
{ |
||||
|
public interface IDaprApiDescriptionFinder |
||||
|
{ |
||||
|
Task<ActionApiDescriptionModel> FindActionAsync(string appId, Type serviceType, MethodInfo invocationMethod); |
||||
|
|
||||
|
Task<ApplicationApiDescriptionModel> GetApiDescriptionAsync(string appId); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,144 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using System; |
||||
|
using System.Collections; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Globalization; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.Http.Client.DynamicProxying; |
||||
|
using Volo.Abp.Http.Modeling; |
||||
|
using Volo.Abp.Http.ProxyScripting.Generators; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Dapr.Client.DynamicProxying |
||||
|
{ |
||||
|
internal static class UrlBuilder |
||||
|
{ |
||||
|
public static string GenerateUrlWithParameters(ActionApiDescriptionModel action, IReadOnlyDictionary<string, object> methodArguments, ApiVersionInfo apiVersion) |
||||
|
{ |
||||
|
var urlBuilder = new StringBuilder(action.Url); |
||||
|
|
||||
|
ReplacePathVariables(urlBuilder, action.Parameters, methodArguments, apiVersion); |
||||
|
AddQueryStringParameters(urlBuilder, action.Parameters, methodArguments, apiVersion); |
||||
|
|
||||
|
return urlBuilder.ToString(); |
||||
|
} |
||||
|
|
||||
|
private static void ReplacePathVariables(StringBuilder urlBuilder, IList<ParameterApiDescriptionModel> actionParameters, IReadOnlyDictionary<string, object> methodArguments, ApiVersionInfo apiVersion) |
||||
|
{ |
||||
|
var pathParameters = actionParameters |
||||
|
.Where(p => p.BindingSourceId == ParameterBindingSources.Path) |
||||
|
.ToArray(); |
||||
|
|
||||
|
if (!pathParameters.Any()) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (pathParameters.Any(p => p.Name == "apiVersion")) |
||||
|
{ |
||||
|
urlBuilder = urlBuilder.Replace("{apiVersion}", apiVersion.Version); |
||||
|
} |
||||
|
|
||||
|
foreach (var pathParameter in pathParameters.Where(p => p.Name != "apiVersion")) //TODO: Constant!
|
||||
|
{ |
||||
|
var value = HttpActionParameterHelper.FindParameterValue(methodArguments, pathParameter); |
||||
|
|
||||
|
if (value == null) |
||||
|
{ |
||||
|
if (pathParameter.IsOptional) |
||||
|
{ |
||||
|
urlBuilder = urlBuilder.Replace($"{{{pathParameter.Name}}}", ""); |
||||
|
} |
||||
|
else if (pathParameter.DefaultValue != null) |
||||
|
{ |
||||
|
urlBuilder = urlBuilder.Replace($"{{{pathParameter.Name}}}", pathParameter.DefaultValue.ToString()); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
throw new AbpException($"Missing path parameter value for {pathParameter.Name} ({pathParameter.NameOnMethod})"); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
urlBuilder = urlBuilder.Replace($"{{{pathParameter.Name}}}", value.ToString()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static void AddQueryStringParameters(StringBuilder urlBuilder, IList<ParameterApiDescriptionModel> actionParameters, IReadOnlyDictionary<string, object> methodArguments, ApiVersionInfo apiVersion) |
||||
|
{ |
||||
|
var queryStringParameters = actionParameters |
||||
|
.Where(p => p.BindingSourceId.IsIn(ParameterBindingSources.ModelBinding, ParameterBindingSources.Query)) |
||||
|
.ToArray(); |
||||
|
|
||||
|
var isFirstParam = true; |
||||
|
|
||||
|
foreach (var queryStringParameter in queryStringParameters) |
||||
|
{ |
||||
|
var value = HttpActionParameterHelper.FindParameterValue(methodArguments, queryStringParameter); |
||||
|
if (value == null) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if (AddQueryStringParameter(urlBuilder, isFirstParam, queryStringParameter.Name, value)) |
||||
|
{ |
||||
|
isFirstParam = false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (apiVersion.ShouldSendInQueryString()) |
||||
|
{ |
||||
|
AddQueryStringParameter(urlBuilder, isFirstParam, "api-version", apiVersion.Version); //TODO: Constant!
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static bool AddQueryStringParameter( |
||||
|
StringBuilder urlBuilder, |
||||
|
bool isFirstParam, |
||||
|
string name, |
||||
|
[NotNull] object value) |
||||
|
{ |
||||
|
if (value.GetType().IsArray || (value.GetType().IsGenericType && value is IEnumerable)) |
||||
|
{ |
||||
|
var index = 0; |
||||
|
foreach (var item in (IEnumerable)value) |
||||
|
{ |
||||
|
if (index == 0) |
||||
|
{ |
||||
|
urlBuilder.Append(isFirstParam ? "?" : "&"); |
||||
|
} |
||||
|
urlBuilder.Append(name + $"[{index++}]=" + System.Net.WebUtility.UrlEncode(ConvertValueToString(item)) + "&"); |
||||
|
} |
||||
|
|
||||
|
if (index > 0) |
||||
|
{ |
||||
|
//remove & at the end of the urlBuilder.
|
||||
|
urlBuilder.Remove(urlBuilder.Length - 1, 1); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
urlBuilder.Append(isFirstParam ? "?" : "&"); |
||||
|
urlBuilder.Append(name + "=" + System.Net.WebUtility.UrlEncode(ConvertValueToString(value))); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
private static string ConvertValueToString([NotNull] object value) |
||||
|
{ |
||||
|
using (CultureHelper.Use(CultureInfo.InvariantCulture)) |
||||
|
{ |
||||
|
if (value is DateTime dateTimeValue) |
||||
|
{ |
||||
|
return dateTimeValue.ToUniversalTime().ToString("u"); |
||||
|
} |
||||
|
|
||||
|
return value.ToString(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using Dapr.Client; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Dapr.Client |
||||
|
{ |
||||
|
public interface IDaprClientFactory |
||||
|
{ |
||||
|
DaprClient Create(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,114 @@ |
|||||
|
using Castle.DynamicProxy; |
||||
|
using JetBrains.Annotations; |
||||
|
using LINGYUN.Abp.Dapr.Client; |
||||
|
using LINGYUN.Abp.Dapr.Client.DynamicProxying; |
||||
|
using Microsoft.Extensions.DependencyInjection.Extensions; |
||||
|
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 ServiceCollectionDynamicDaprClientProxyExtensions |
||||
|
{ |
||||
|
private static readonly ProxyGenerator ProxyGeneratorInstance = new ProxyGenerator(); |
||||
|
|
||||
|
public static IServiceCollection AddDaprClient( |
||||
|
[NotNull] this IServiceCollection services) |
||||
|
{ |
||||
|
Check.NotNull(services, nameof(services)); |
||||
|
|
||||
|
services.TryAddSingleton(provider => provider.GetRequiredService<IDaprClientFactory>().Create()); |
||||
|
|
||||
|
return services; |
||||
|
} |
||||
|
|
||||
|
public static IServiceCollection AddDaprClientProxies( |
||||
|
[NotNull] this IServiceCollection services, |
||||
|
[NotNull] Assembly assembly, |
||||
|
[NotNull] string remoteServiceConfigurationName = DaprRemoteServiceConfigurationDictionary.DefaultName, |
||||
|
bool asDefaultServices = true) |
||||
|
{ |
||||
|
Check.NotNull(services, nameof(assembly)); |
||||
|
|
||||
|
var serviceTypes = assembly.GetTypes().Where(IsSuitableForDynamicActorProxying).ToArray(); |
||||
|
|
||||
|
foreach (var serviceType in serviceTypes) |
||||
|
{ |
||||
|
services.AddDaprClientProxy( |
||||
|
serviceType, |
||||
|
remoteServiceConfigurationName, |
||||
|
asDefaultServices |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
return services; |
||||
|
} |
||||
|
|
||||
|
public static IServiceCollection AddDaprClientProxy<T>( |
||||
|
[NotNull] this IServiceCollection services, |
||||
|
[NotNull] string remoteServiceConfigurationName = DaprRemoteServiceConfigurationDictionary.DefaultName, |
||||
|
bool asDefaultService = true) |
||||
|
{ |
||||
|
return services.AddDaprClientProxy( |
||||
|
typeof(T), |
||||
|
remoteServiceConfigurationName, |
||||
|
asDefaultService |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
public static IServiceCollection AddDaprClientProxy( |
||||
|
[NotNull] this IServiceCollection services, |
||||
|
[NotNull] Type type, |
||||
|
[NotNull] string remoteServiceConfigurationName = DaprRemoteServiceConfigurationDictionary.DefaultName, |
||||
|
bool asDefaultService = true) |
||||
|
{ |
||||
|
Check.NotNull(services, nameof(services)); |
||||
|
Check.NotNull(type, nameof(type)); |
||||
|
Check.NotNullOrWhiteSpace(remoteServiceConfigurationName, nameof(remoteServiceConfigurationName)); |
||||
|
|
||||
|
// AddHttpClientFactory(services, remoteServiceConfigurationName);
|
||||
|
|
||||
|
services.Configure<AbpDaprClientProxyOptions>(options => |
||||
|
{ |
||||
|
options.DaprClientProxies[type] = new DynamicDaprClientProxyConfig(type, remoteServiceConfigurationName); |
||||
|
}); |
||||
|
|
||||
|
var interceptorType = typeof(DynamicDaprClientProxyInterceptor<>).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(IRemoteService).IsAssignableFrom(type); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,51 @@ |
|||||
|
# LINGYUN.Abp.Dapr.Client |
||||
|
|
||||
|
实现了Dapr文档中的服务间调用,项目设计与Volo.Abp.Http.Client一致,通过配置文件即可无缝替代Volo.Abp.Http.Client |
||||
|
|
||||
|
配置参考 [AbpRemoteServiceOptions](https://docs.abp.io/zh-Hans/abp/latest/API/Dynamic-CSharp-API-Clients#abpremoteserviceoptions) |
||||
|
|
||||
|
## 配置使用 |
||||
|
|
||||
|
模块按需引用 |
||||
|
|
||||
|
```csharp |
||||
|
[DependsOn(typeof(AbpDaprClientModule))] |
||||
|
public class YouProjectModule : AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
// 注册代理类似于 Volo.Abp.Http.Client 模块 |
||||
|
context.Services.AddDaprClientProxies( |
||||
|
typeof(YouProjectActorInterfaceModule).Assembly, // 搜索 YouProjectActorInterfaceModule 模块下的远程服务定义 |
||||
|
RemoteServiceName |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
## 配置项说明 |
||||
|
|
||||
|
* AbpDaprClientOptions.GrpcEndpoint Dapr暴露的Grpc端点, 对应 **DaprClientBuilder.GrpcEndpoint** |
||||
|
* AbpDaprClientOptions.HttpEndpoint Dapr暴露的Http端点, 对应 **DaprClientBuilder.HttpEndpoint** |
||||
|
* AbpDaprClientOptions.GrpcChannelOptions 通过Grpc调用远程服务的配置项, 对应 **DaprClientBuilder.GrpcChannelOptions** |
||||
|
|
||||
|
* AbpDaprRemoteServiceOptions.RemoteServices 配置Dapr.AppId |
||||
|
|
||||
|
```json |
||||
|
|
||||
|
{ |
||||
|
"Dapr": { |
||||
|
"Client": { |
||||
|
"HttpEndpoint": "http://127.0.0.1:50000" |
||||
|
} |
||||
|
}, |
||||
|
"RemoteServices": { |
||||
|
"System": { |
||||
|
"AppId": "myapp" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
``` |
||||
|
|
||||
|
|
||||
|
## 其他 |
||||
Loading…
Reference in new issue