30 changed files with 900 additions and 622 deletions
@ -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.Http.Client" Version="5.0.0-rc.1" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.Wrapper\LINGYUN.Abp.Wrapper.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,13 @@ |
|||
using LINGYUN.Abp.Wrapper; |
|||
using Volo.Abp.Http.Client; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.HttpClient.Wrapper |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpHttpClientModule), |
|||
typeof(AbpWrapperModule))] |
|||
public class AbpHttpClientWrapperModule : AbpModule |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,153 @@ |
|||
using LINGYUN.Abp.Wrapper; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Net.Http; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Content; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Http; |
|||
using Volo.Abp.Http.Client; |
|||
using Volo.Abp.Http.Client.Authentication; |
|||
using Volo.Abp.Http.Client.ClientProxying; |
|||
using Volo.Abp.Http.Client.DynamicProxying; |
|||
|
|||
namespace LINGYUN.Abp.HttpClient.Wrapper |
|||
{ |
|||
[Dependency(ReplaceServices = true)] |
|||
[ExposeServices(typeof(DynamicHttpProxyInterceptorClientProxy<>))] |
|||
public class DynamicHttpProxyInterceptorWrapClientProxy<TService> |
|||
: DynamicHttpProxyInterceptorClientProxy<TService>, ITransientDependency |
|||
{ |
|||
protected IOptions<AbpWrapperOptions> WrapperOptions => LazyServiceProvider.LazyGetRequiredService<IOptions<AbpWrapperOptions>>(); |
|||
|
|||
protected override async Task<T> RequestAsync<T>(ClientProxyRequestContext requestContext) |
|||
{ |
|||
var response = await RequestAndGetResponseAsync(requestContext); |
|||
|
|||
var responseContent = response.Content; |
|||
|
|||
if (typeof(T) == typeof(IRemoteStreamContent) || |
|||
typeof(T) == typeof(RemoteStreamContent)) |
|||
{ |
|||
/* 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(), |
|||
responseContent.Headers?.ContentDisposition?.FileNameStar ?? |
|||
RemoveQuotes(responseContent.Headers?.ContentDisposition?.FileName).ToString(), |
|||
responseContent.Headers?.ContentType?.ToString(), |
|||
responseContent.Headers?.ContentLength); |
|||
} |
|||
|
|||
var stringContent = await responseContent.ReadAsStringAsync(); |
|||
|
|||
if (stringContent.IsNullOrWhiteSpace()) |
|||
{ |
|||
return default; |
|||
} |
|||
|
|||
// 对于包装后的结果需要处理
|
|||
if (response.Headers.Contains(AbpHttpWrapConsts.AbpWrapResult)) |
|||
{ |
|||
var wrapResult = JsonSerializer.Deserialize<WrapResult<T>>(stringContent); |
|||
|
|||
ThrowExceptionForResponse(wrapResult); |
|||
|
|||
if (typeof(T) == typeof(string)) |
|||
{ |
|||
return (T)(object)wrapResult.Result; |
|||
} |
|||
|
|||
return wrapResult.Result; |
|||
} |
|||
|
|||
if (typeof(T) == typeof(string)) |
|||
{ |
|||
return (T)(object)stringContent; |
|||
} |
|||
|
|||
if (stringContent.IsNullOrWhiteSpace()) |
|||
{ |
|||
return default; |
|||
} |
|||
|
|||
return JsonSerializer.Deserialize<T>(stringContent); |
|||
} |
|||
|
|||
public override async Task<HttpContent> CallRequestAsync(ClientProxyRequestContext requestContext) |
|||
{ |
|||
var response = await RequestAndGetResponseAsync(requestContext); |
|||
// 对于包装后的结果需要处理
|
|||
if (response.Headers.Contains(AbpHttpWrapConsts.AbpWrapResult)) |
|||
{ |
|||
var stringContent = await response.Content.ReadAsStringAsync(); |
|||
var wrapResult = JsonSerializer.Deserialize<WrapResult>(stringContent); |
|||
|
|||
ThrowExceptionForResponse(wrapResult); |
|||
} |
|||
|
|||
return response.Content; |
|||
} |
|||
|
|||
protected virtual void ThrowExceptionForResponse<T>(WrapResult<T> wrapResult) |
|||
{ |
|||
if (!string.Equals(wrapResult.Code, WrapperOptions.Value.CodeWithSuccess)) |
|||
{ |
|||
var errorInfo = new RemoteServiceErrorInfo( |
|||
wrapResult.Message, |
|||
wrapResult.Details, |
|||
wrapResult.Code); |
|||
throw new AbpRemoteCallException(errorInfo) |
|||
{ |
|||
HttpStatusCode = (int)WrapperOptions.Value.HttpStatusCode |
|||
}; |
|||
} |
|||
} |
|||
|
|||
protected virtual async Task<HttpResponseMessage> RequestAndGetResponseAsync(ClientProxyRequestContext requestContext) |
|||
{ |
|||
var clientConfig = ClientOptions.Value.HttpClientProxies.GetOrDefault(requestContext.ServiceType) ?? throw new AbpException($"Could not get HttpClientProxyConfig for {requestContext.ServiceType.FullName}."); |
|||
var remoteServiceConfig = await RemoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync(clientConfig.RemoteServiceName); |
|||
|
|||
var client = HttpClientFactory.Create(clientConfig.RemoteServiceName); |
|||
|
|||
var apiVersion = await GetApiVersionInfoAsync(requestContext); |
|||
var url = remoteServiceConfig.BaseUrl.EnsureEndsWith('/') + await GetUrlWithParametersAsync(requestContext, apiVersion); |
|||
|
|||
var requestMessage = new HttpRequestMessage(requestContext.Action.GetHttpMethod(), url) |
|||
{ |
|||
Content = await ClientProxyRequestPayloadBuilder.BuildContentAsync(requestContext.Action, requestContext.Arguments, JsonSerializer, apiVersion) |
|||
}; |
|||
|
|||
AddHeaders(requestContext.Arguments, requestContext.Action, requestMessage, apiVersion); |
|||
|
|||
if (requestContext.Action.AllowAnonymous != true) |
|||
{ |
|||
await ClientAuthenticator.Authenticate( |
|||
new RemoteServiceHttpClientAuthenticateContext( |
|||
client, |
|||
requestMessage, |
|||
remoteServiceConfig, |
|||
clientConfig.RemoteServiceName |
|||
) |
|||
); |
|||
} |
|||
|
|||
var response = await client.SendAsync( |
|||
requestMessage, |
|||
HttpCompletionOption.ResponseHeadersRead /*this will buffer only the headers, the content will be used as a stream*/, |
|||
GetCancellationToken(requestContext.Arguments) |
|||
); |
|||
|
|||
if (!response.IsSuccessStatusCode) |
|||
{ |
|||
await ThrowExceptionForResponseAsync(response); |
|||
} |
|||
|
|||
return response; |
|||
} |
|||
} |
|||
} |
|||
@ -1,4 +1,4 @@ |
|||
namespace LINGYUN.Abp.AspNetCore.Mvc.Wrapper |
|||
namespace LINGYUN.Abp.Wrapper |
|||
{ |
|||
public static class AbpHttpWrapConsts |
|||
{ |
|||
@ -1,54 +1,61 @@ |
|||
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); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
using Dapr.Actors; |
|||
using Dapr.Actors.Runtime; |
|||
using LINGYUN.Abp.Wrapper; |
|||
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), |
|||
typeof(AbpWrapperModule))] |
|||
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(); |
|||
}); |
|||
}); |
|||
|
|||
Configure<AbpWrapperOptions>(options => |
|||
{ |
|||
options.IgnoreBaseTypes.TryAdd<IActor>(); |
|||
}); |
|||
} |
|||
|
|||
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); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -1,24 +1,24 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Http.Client; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Dapr.Actors |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpHttpClientModule) |
|||
)] |
|||
public class AbpDaprActorsModule : AbpModule |
|||
{ |
|||
/// <summary>
|
|||
/// 与AbpHttpClient集成,创建一个命名HttpClient
|
|||
/// </summary>
|
|||
internal const string DaprHttpClient = "_AbpDaprActorsClient"; |
|||
|
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var configuration = context.Services.GetConfiguration(); |
|||
Configure<AbpDaprRemoteServiceOptions>(configuration); |
|||
context.Services.AddHttpClient(DaprHttpClient); |
|||
} |
|||
} |
|||
} |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Http.Client; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Dapr.Actors |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpHttpClientModule) |
|||
)] |
|||
public class AbpDaprActorsModule : AbpModule |
|||
{ |
|||
/// <summary>
|
|||
/// 与AbpHttpClient集成,创建一个命名HttpClient
|
|||
/// </summary>
|
|||
internal const string DaprHttpClient = "_AbpDaprActorsClient"; |
|||
|
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var configuration = context.Services.GetConfiguration(); |
|||
Configure<AbpDaprRemoteServiceOptions>(configuration); |
|||
context.Services.AddHttpClient(DaprHttpClient); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -1,312 +1,342 @@ |
|||
using Dapr.Client; |
|||
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.Authentication; |
|||
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 IDaprClientFactory DaprClientFactory { get; } |
|||
protected ICancellationTokenProvider CancellationTokenProvider { get; } |
|||
protected ICorrelationIdProvider CorrelationIdProvider { get; } |
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
protected AbpCorrelationIdOptions AbpCorrelationIdOptions { get; } |
|||
protected IDynamicProxyHttpClientFactory HttpClientFactory { get; } |
|||
protected IDaprApiDescriptionFinder ApiDescriptionFinder { get; } |
|||
protected AbpDaprRemoteServiceOptions AbpRemoteServiceOptions { get; } |
|||
protected AbpDaprClientProxyOptions ClientProxyOptions { get; } |
|||
protected IJsonSerializer JsonSerializer { get; } |
|||
protected IRemoteServiceHttpClientAuthenticator 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( |
|||
IDaprClientFactory daprClientFactory, |
|||
IOptions<AbpDaprClientProxyOptions> clientProxyOptions, |
|||
IOptionsSnapshot<AbpDaprRemoteServiceOptions> remoteServiceOptions, |
|||
IDaprApiDescriptionFinder apiDescriptionFinder, |
|||
IJsonSerializer jsonSerializer, |
|||
IDynamicProxyHttpClientFactory dynamicProxyHttpClientFactory, |
|||
IRemoteServiceHttpClientAuthenticator clientAuthenticator, |
|||
ICancellationTokenProvider cancellationTokenProvider, |
|||
ICorrelationIdProvider correlationIdProvider, |
|||
IOptions<AbpCorrelationIdOptions> correlationIdOptions, |
|||
ICurrentTenant currentTenant) |
|||
{ |
|||
DaprClientFactory = daprClientFactory; |
|||
CancellationTokenProvider = cancellationTokenProvider; |
|||
CorrelationIdProvider = correlationIdProvider; |
|||
CurrentTenant = currentTenant; |
|||
HttpClientFactory = dynamicProxyHttpClientFactory; |
|||
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( |
|||
clientConfig.RemoteServiceName, |
|||
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 daprClient = DaprClientFactory.CreateClient(clientConfig.RemoteServiceName); |
|||
var requestMessage = daprClient.CreateInvokeMethodRequest( |
|||
action.GetHttpMethod(), |
|||
remoteServiceConfig.AppId, |
|||
methodName); |
|||
|
|||
requestMessage.Content = RequestPayloadBuilder.BuildContent(action, invocation.ArgumentsDictionary, JsonSerializer, apiVersion); |
|||
|
|||
AddHeaders(invocation, action, requestMessage, apiVersion); |
|||
|
|||
var httpClient = HttpClientFactory.Create(AbpDaprClientModule.DaprHttpClient); |
|||
await ClientAuthenticator.Authenticate( |
|||
new RemoteServiceHttpClientAuthenticateContext( |
|||
httpClient, |
|||
requestMessage, |
|||
remoteServiceConfig, |
|||
clientConfig.RemoteServiceName |
|||
) |
|||
); |
|||
// 其他库可能将授权标头写入到HttpClient中
|
|||
if (requestMessage.Headers.Authorization == null && |
|||
httpClient.DefaultRequestHeaders.Authorization != null) |
|||
{ |
|||
requestMessage.Headers.Authorization = httpClient.DefaultRequestHeaders.Authorization; |
|||
} |
|||
|
|||
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; |
|||
} |
|||
} |
|||
} |
|||
using Dapr.Client; |
|||
using LINGYUN.Abp.Wrapper; |
|||
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.Authentication; |
|||
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 IDaprClientFactory DaprClientFactory { get; } |
|||
protected ICancellationTokenProvider CancellationTokenProvider { get; } |
|||
protected ICorrelationIdProvider CorrelationIdProvider { get; } |
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
protected AbpCorrelationIdOptions AbpCorrelationIdOptions { get; } |
|||
protected IDynamicProxyHttpClientFactory HttpClientFactory { get; } |
|||
protected IDaprApiDescriptionFinder ApiDescriptionFinder { get; } |
|||
protected AbpDaprRemoteServiceOptions AbpRemoteServiceOptions { get; } |
|||
protected AbpDaprClientProxyOptions ClientProxyOptions { get; } |
|||
protected AbpWrapperOptions WrapperOptions { get; } |
|||
protected IJsonSerializer JsonSerializer { get; } |
|||
protected IRemoteServiceHttpClientAuthenticator 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( |
|||
IDaprClientFactory daprClientFactory, |
|||
IOptions<AbpDaprClientProxyOptions> clientProxyOptions, |
|||
IOptionsSnapshot<AbpDaprRemoteServiceOptions> remoteServiceOptions, |
|||
IDaprApiDescriptionFinder apiDescriptionFinder, |
|||
IJsonSerializer jsonSerializer, |
|||
IDynamicProxyHttpClientFactory dynamicProxyHttpClientFactory, |
|||
IRemoteServiceHttpClientAuthenticator clientAuthenticator, |
|||
ICancellationTokenProvider cancellationTokenProvider, |
|||
ICorrelationIdProvider correlationIdProvider, |
|||
IOptions<AbpCorrelationIdOptions> correlationIdOptions, |
|||
IOptions<AbpWrapperOptions> wrapperOptions, |
|||
ICurrentTenant currentTenant) |
|||
{ |
|||
DaprClientFactory = daprClientFactory; |
|||
CancellationTokenProvider = cancellationTokenProvider; |
|||
CorrelationIdProvider = correlationIdProvider; |
|||
CurrentTenant = currentTenant; |
|||
HttpClientFactory = dynamicProxyHttpClientFactory; |
|||
AbpCorrelationIdOptions = correlationIdOptions.Value; |
|||
ApiDescriptionFinder = apiDescriptionFinder; |
|||
JsonSerializer = jsonSerializer; |
|||
ClientAuthenticator = clientAuthenticator; |
|||
ClientProxyOptions = clientProxyOptions.Value; |
|||
AbpRemoteServiceOptions = remoteServiceOptions.Value; |
|||
WrapperOptions = wrapperOptions.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 response = await MakeRequestAsync(invocation); |
|||
var responseContent = response.Content; |
|||
|
|||
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 (stringContent.IsNullOrWhiteSpace()) |
|||
{ |
|||
return default; |
|||
} |
|||
|
|||
// 对于包装后的结果需要处理
|
|||
if (response.Headers.Contains(AbpHttpWrapConsts.AbpWrapResult)) |
|||
{ |
|||
var wrapResult = JsonSerializer.Deserialize<WrapResult<T>>(stringContent); |
|||
|
|||
if (!string.Equals(wrapResult.Code, WrapperOptions.CodeWithSuccess)) |
|||
{ |
|||
var errorInfo = new RemoteServiceErrorInfo( |
|||
wrapResult.Message, |
|||
wrapResult.Details, |
|||
wrapResult.Code); |
|||
throw new AbpRemoteCallException(errorInfo) |
|||
{ |
|||
HttpStatusCode = (int)WrapperOptions.HttpStatusCode |
|||
}; |
|||
} |
|||
|
|||
if (typeof(T) == typeof(string)) |
|||
{ |
|||
return (T)(object)wrapResult.Result; |
|||
} |
|||
|
|||
return wrapResult.Result; |
|||
} |
|||
|
|||
if (typeof(T) == typeof(string)) |
|||
{ |
|||
return (T)(object)stringContent; |
|||
} |
|||
|
|||
return JsonSerializer.Deserialize<T>(stringContent); |
|||
} |
|||
|
|||
private async Task<HttpResponseMessage> 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( |
|||
clientConfig.RemoteServiceName, |
|||
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 daprClient = DaprClientFactory.CreateClient(clientConfig.RemoteServiceName); |
|||
var requestMessage = daprClient.CreateInvokeMethodRequest( |
|||
action.GetHttpMethod(), |
|||
remoteServiceConfig.AppId, |
|||
methodName); |
|||
|
|||
requestMessage.Content = RequestPayloadBuilder.BuildContent(action, invocation.ArgumentsDictionary, JsonSerializer, apiVersion); |
|||
|
|||
AddHeaders(invocation, action, requestMessage, apiVersion); |
|||
|
|||
var httpClient = HttpClientFactory.Create(AbpDaprClientModule.DaprHttpClient); |
|||
await ClientAuthenticator.Authenticate( |
|||
new RemoteServiceHttpClientAuthenticateContext( |
|||
httpClient, |
|||
requestMessage, |
|||
remoteServiceConfig, |
|||
clientConfig.RemoteServiceName |
|||
) |
|||
); |
|||
// 其他库可能将授权标头写入到HttpClient中
|
|||
if (requestMessage.Headers.Authorization == null && |
|||
httpClient.DefaultRequestHeaders.Authorization != null) |
|||
{ |
|||
requestMessage.Headers.Authorization = httpClient.DefaultRequestHeaders.Authorization; |
|||
} |
|||
|
|||
var response = await daprClient.InvokeMethodWithResponseAsync(requestMessage, GetCancellationToken()); |
|||
|
|||
if (!response.IsSuccessStatusCode) |
|||
{ |
|||
await ThrowExceptionForResponseAsync(response); |
|||
} |
|||
|
|||
return response; |
|||
} |
|||
|
|||
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; |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -1,70 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Net; |
|||
using Volo.Abp.Collections; |
|||
|
|||
namespace LINGYUN.Abp.AspNetCore.Mvc.Wrapper |
|||
{ |
|||
public class AbpAspNetCoreMvcWrapperOptions |
|||
{ |
|||
/// <summary>
|
|||
/// 是否启用包装器
|
|||
/// </summary>
|
|||
public bool IsEnabled { get; set; } |
|||
/// <summary>
|
|||
/// 资源有效时返回代码
|
|||
/// 默认:0
|
|||
/// </summary>
|
|||
public string CodeWithFound { get; set; } |
|||
/// <summary>
|
|||
/// 资源为空时返回代码
|
|||
/// 默认:404
|
|||
/// </summary>
|
|||
public Func<IServiceProvider, string> CodeWithEmptyResult { get; set; } |
|||
/// <summary>
|
|||
/// 资源为空时返回错误消息
|
|||
/// </summary>
|
|||
public Func<IServiceProvider, string> MessageWithEmptyResult { get; set; } |
|||
/// <summary>
|
|||
/// 包装后的返回状态码
|
|||
/// 默认:200 HttpStatusCode.OK
|
|||
/// </summary>
|
|||
public HttpStatusCode HttpStatusCode { get; set; } |
|||
/// <summary>
|
|||
/// 忽略Url开头类型
|
|||
/// </summary>
|
|||
public IList<string> IgnorePrefixUrls { get; } |
|||
/// <summary>
|
|||
/// 忽略指定命名空间
|
|||
/// </summary>
|
|||
public IList<string> IgnoreNamespaces { get; } |
|||
/// <summary>
|
|||
/// 忽略控制器
|
|||
/// </summary>
|
|||
public ITypeList IgnoreControllers { get; } |
|||
/// <summary>
|
|||
/// 忽略返回值
|
|||
/// </summary>
|
|||
public ITypeList IgnoreReturnTypes { get; } |
|||
/// <summary>
|
|||
/// 忽略异常
|
|||
/// </summary>
|
|||
public ITypeList<Exception> IgnoreExceptions { get; } |
|||
|
|||
public AbpAspNetCoreMvcWrapperOptions() |
|||
{ |
|||
CodeWithFound = "0"; |
|||
HttpStatusCode = HttpStatusCode.OK; |
|||
|
|||
IgnorePrefixUrls = new List<string>(); |
|||
IgnoreNamespaces = new List<string>(); |
|||
|
|||
IgnoreControllers = new TypeList(); |
|||
IgnoreReturnTypes = new TypeList(); |
|||
IgnoreExceptions = new TypeList<Exception>(); |
|||
|
|||
CodeWithEmptyResult = (_) => "404"; |
|||
MessageWithEmptyResult = (_) => "Not Found"; |
|||
} |
|||
} |
|||
} |
|||
@ -1,39 +1,51 @@ |
|||
using LINGYUN.Abp.Dapr.Actors.AspNetCore; |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Dapr |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpAspNetCoreMvcModule), |
|||
typeof(AbpDaprActorsAspNetCoreModule) |
|||
)] |
|||
public class AbpDaprAspNetCoreTestHostModule : AbpModule |
|||
{ |
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<IMvcBuilder>(builder => |
|||
{ |
|||
builder.AddApplicationPart(typeof(AbpDaprAspNetCoreTestHostModule).Assembly); |
|||
}); |
|||
} |
|||
|
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var hostingEnvironment = context.Services.GetHostingEnvironment(); |
|||
var configuration = context.Services.GetConfiguration(); |
|||
} |
|||
|
|||
public override void OnApplicationInitialization(ApplicationInitializationContext context) |
|||
{ |
|||
var app = context.GetApplicationBuilder(); |
|||
|
|||
app.UseRouting(); |
|||
app.UseAuditing(); |
|||
app.UseConfiguredEndpoints(); |
|||
} |
|||
} |
|||
} |
|||
using LINGYUN.Abp.AspNetCore.Mvc.Wrapper; |
|||
using LINGYUN.Abp.Dapr.Actors.AspNetCore; |
|||
using LINGYUN.Abp.Dapr.ServiceInvocation; |
|||
using LINGYUN.Abp.Wrapper; |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Dapr |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpDaprActorsAspNetCoreModule), |
|||
typeof(AbpAspNetCoreMvcWrapperModule) |
|||
)] |
|||
public class AbpDaprAspNetCoreTestHostModule : AbpModule |
|||
{ |
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<IMvcBuilder>(builder => |
|||
{ |
|||
builder.AddApplicationPart(typeof(AbpDaprAspNetCoreTestHostModule).Assembly); |
|||
}); |
|||
} |
|||
|
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var hostingEnvironment = context.Services.GetHostingEnvironment(); |
|||
var configuration = context.Services.GetConfiguration(); |
|||
|
|||
Configure<AbpWrapperOptions>(options => |
|||
{ |
|||
options.IsEnabled = true; |
|||
|
|||
options.IgnoreReturnTypes.TryAdd<NameValue>(); |
|||
options.IgnoreReturnTypes.TryAdd<ListResultDto<NameValue>>(); |
|||
}); |
|||
} |
|||
|
|||
public override void OnApplicationInitialization(ApplicationInitializationContext context) |
|||
{ |
|||
var app = context.GetApplicationBuilder(); |
|||
|
|||
app.UseRouting(); |
|||
app.UseAuditing(); |
|||
app.UseConfiguredEndpoints(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -1,41 +1,53 @@ |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
|
|||
namespace LINGYUN.Abp.Dapr.ServiceInvocation |
|||
{ |
|||
[RemoteService(Name = "TestDapr")] |
|||
[Route("api/dapr/test")] |
|||
public class TestAppService : AbpController, ITestAppService |
|||
{ |
|||
private static int _inctement; |
|||
private readonly List<NameValue> _cache = new List<NameValue> |
|||
{ |
|||
new NameValue("name1", "value1"), |
|||
new NameValue("name2", "value2"), |
|||
new NameValue("name3", "value3"), |
|||
new NameValue("name4", "value4"), |
|||
new NameValue("name5", "value5") |
|||
}; |
|||
|
|||
[HttpGet] |
|||
public Task<ListResultDto<NameValue>> GetAsync() |
|||
{ |
|||
return Task.FromResult(new ListResultDto<NameValue>(_cache)); |
|||
} |
|||
|
|||
[HttpPut] |
|||
public Task<NameValue> UpdateAsync() |
|||
{ |
|||
Interlocked.Increment(ref _inctement); |
|||
|
|||
_cache[0].Value = $"value:updated:{_inctement}"; |
|||
|
|||
return Task.FromResult(_cache[0]); |
|||
} |
|||
} |
|||
} |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
|
|||
namespace LINGYUN.Abp.Dapr.ServiceInvocation |
|||
{ |
|||
[RemoteService(Name = "TestDapr")] |
|||
[Route("api/dapr/test")] |
|||
public class TestAppService : AbpController, ITestAppService |
|||
{ |
|||
private static int _inctement; |
|||
private readonly List<NameValue> _cache = new List<NameValue> |
|||
{ |
|||
new NameValue("name1", "value1"), |
|||
new NameValue("name2", "value2"), |
|||
new NameValue("name3", "value3"), |
|||
new NameValue("name4", "value4"), |
|||
new NameValue("name5", "value5") |
|||
}; |
|||
|
|||
[HttpGet] |
|||
public Task<ListResultDto<NameValue>> GetAsync() |
|||
{ |
|||
return Task.FromResult(new ListResultDto<NameValue>(_cache)); |
|||
} |
|||
|
|||
[HttpPut] |
|||
public Task<NameValue> UpdateAsync() |
|||
{ |
|||
Interlocked.Increment(ref _inctement); |
|||
|
|||
_cache[0].Value = $"value:updated:{_inctement}"; |
|||
|
|||
return Task.FromResult(_cache[0]); |
|||
} |
|||
|
|||
[HttpGet] |
|||
[Route("{name}")] |
|||
public Task<TestNeedWrapObject> GetWrapedAsync(string name) |
|||
{ |
|||
var obj = new TestNeedWrapObject |
|||
{ |
|||
Name = name |
|||
}; |
|||
|
|||
return Task.FromResult(obj); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -1,33 +1,41 @@ |
|||
using LINGYUN.Abp.Dapr.ServiceInvocation; |
|||
using Shouldly; |
|||
using System.Threading.Tasks; |
|||
using Xunit; |
|||
|
|||
namespace LINGYUN.Abp.Dapr.Client.Tests |
|||
{ |
|||
public class TestAppServiceTests : AbpDaptClientTestBase |
|||
{ |
|||
private readonly ITestAppService _service; |
|||
|
|||
public TestAppServiceTests() |
|||
{ |
|||
_service = GetRequiredService<ITestAppService>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Get_Result_Items_Count_Should_5() |
|||
{ |
|||
var result = await _service.GetAsync(); |
|||
|
|||
result.Items.Count.ShouldBe(5); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Update_Result_Value_Should_Value_Updated_1() |
|||
{ |
|||
var result = await _service.UpdateAsync(); |
|||
|
|||
result.Value.ShouldBe("value:updated:1"); |
|||
} |
|||
} |
|||
} |
|||
using LINGYUN.Abp.Dapr.ServiceInvocation; |
|||
using Shouldly; |
|||
using System.Threading.Tasks; |
|||
using Xunit; |
|||
|
|||
namespace LINGYUN.Abp.Dapr.Client.Tests |
|||
{ |
|||
public class TestAppServiceTests : AbpDaptClientTestBase |
|||
{ |
|||
private readonly ITestAppService _service; |
|||
|
|||
public TestAppServiceTests() |
|||
{ |
|||
_service = GetRequiredService<ITestAppService>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Get_Result_Items_Count_Should_5() |
|||
{ |
|||
var result = await _service.GetAsync(); |
|||
|
|||
result.Items.Count.ShouldBe(5); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Get_Wraped_Object() |
|||
{ |
|||
var result = await _service.GetWrapedAsync("Test"); |
|||
|
|||
result.Name.ShouldBe("Test"); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Update_Result_Value_Should_Value_Updated_1() |
|||
{ |
|||
var result = await _service.UpdateAsync(); |
|||
|
|||
result.Value.ShouldBe("value:updated:1"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -1,11 +1,12 @@ |
|||
using Volo.Abp.Application; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Dapr |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpDddApplicationContractsModule))] |
|||
public class AbpDaprTestModule : AbpModule |
|||
{ |
|||
} |
|||
} |
|||
using Volo.Abp.Application; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Dapr |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpDddApplicationContractsModule))] |
|||
public class AbpDaprTestModule : AbpModule |
|||
{ |
|||
|
|||
} |
|||
} |
|||
|
|||
@ -1,14 +1,16 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace LINGYUN.Abp.Dapr.ServiceInvocation |
|||
{ |
|||
public interface ITestAppService : IApplicationService |
|||
{ |
|||
Task<ListResultDto<NameValue>> GetAsync(); |
|||
|
|||
Task<NameValue> UpdateAsync(); |
|||
} |
|||
} |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace LINGYUN.Abp.Dapr.ServiceInvocation |
|||
{ |
|||
public interface ITestAppService : IApplicationService |
|||
{ |
|||
Task<ListResultDto<NameValue>> GetAsync(); |
|||
|
|||
Task<NameValue> UpdateAsync(); |
|||
|
|||
Task<TestNeedWrapObject> GetWrapedAsync(string name); |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,7 @@ |
|||
namespace LINGYUN.Abp.Dapr |
|||
{ |
|||
public class TestNeedWrapObject |
|||
{ |
|||
public string Name { get; set; } |
|||
} |
|||
} |
|||
Loading…
Reference in new issue