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,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