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 |
public static class AbpHttpWrapConsts |
||||
{ |
{ |
||||
@ -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"; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace LINGYUN.Abp.Dapr |
||||
|
{ |
||||
|
public class TestNeedWrapObject |
||||
|
{ |
||||
|
public string Name { get; set; } |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue