From 43c4d80b6dcd0531f58129d2a7750d8a1ba244b2 Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 4 Oct 2021 22:59:28 +0800 Subject: [PATCH 1/6] Add `IObjectToQueryString`. --- .../Volo/Abp/Http/IObjectToQueryString.cs | 9 +++ .../Volo/Abp/Reflection/ReflectionHelper.cs | 7 +- .../Client/ClientProxying/ClientProxyBase.cs | 4 +- .../ClientProxying/ClientProxyUrlBuilder.cs | 81 +++++++++++++++---- .../PersonAppServiceClientProxy_Tests.cs | 27 +++++++ .../Dto/GetParamsFromQueryInput.cs | 44 ++++++++++ .../TestApp/Application/IPeopleAppService.cs | 1 + .../TestApp/Application/PeopleAppService.cs | 8 ++ 8 files changed, 163 insertions(+), 18 deletions(-) create mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/Http/IObjectToQueryString.cs create mode 100644 framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/GetParamsFromQueryInput.cs diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Http/IObjectToQueryString.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Http/IObjectToQueryString.cs new file mode 100644 index 0000000000..19696e546c --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Http/IObjectToQueryString.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Volo.Abp.Http +{ + public interface IObjectToQueryString + { + Task ConvertAsync(TValue value); + } +} diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/ReflectionHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/ReflectionHelper.cs index 132c198aa9..e0b999ac3d 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/ReflectionHelper.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/ReflectionHelper.cs @@ -134,7 +134,7 @@ namespace Volo.Abp.Reflection var currentType = objectType; var objectPath = currentType.FullName; var absolutePropertyPath = propertyPath; - if (objectPath != null && absolutePropertyPath.StartsWith(objectPath)) + if (objectPath != null && absolutePropertyPath.StartsWith(objectPath)) { absolutePropertyPath = absolutePropertyPath.Replace(objectPath + ".", ""); } @@ -144,7 +144,10 @@ namespace Volo.Abp.Reflection var property = currentType.GetProperty(propertyName); if (property != null) { - value = property.GetValue(value, null); + if (value != null) + { + value = property.GetValue(value, null); + } currentType = property.PropertyType; } else diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyBase.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyBase.cs index 5254b10238..68ebc87543 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyBase.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyBase.cs @@ -156,9 +156,9 @@ namespace Volo.Abp.Http.Client.ClientProxying return new ApiVersionInfo(versionParam?.BindingSourceId, apiVersion); } - protected virtual Task GetUrlWithParametersAsync(ClientProxyRequestContext requestContext, ApiVersionInfo apiVersion) + protected virtual async Task GetUrlWithParametersAsync(ClientProxyRequestContext requestContext, ApiVersionInfo apiVersion) { - return Task.FromResult(ClientProxyUrlBuilder.GenerateUrlWithParameters(requestContext.Action, requestContext.Arguments, apiVersion)); + return await ClientProxyUrlBuilder.GenerateUrlWithParametersAsync(requestContext.Action, requestContext.Arguments, apiVersion); } protected virtual Task GetHttpContentAsync(ClientProxyRequestContext requestContext, ApiVersionInfo apiVersion) diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs index e6a9b8ff71..1eb07e88cf 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs @@ -3,8 +3,11 @@ using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Reflection; using System.Text; +using System.Threading.Tasks; using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; using Volo.Abp.DependencyInjection; using Volo.Abp.Http.Client.Proxying; using Volo.Abp.Http.Modeling; @@ -15,7 +18,23 @@ namespace Volo.Abp.Http.Client.ClientProxying { public class ClientProxyUrlBuilder : ITransientDependency { - public string GenerateUrlWithParameters(ActionApiDescriptionModel action, IReadOnlyDictionary methodArguments, ApiVersionInfo apiVersion) + protected static MethodInfo CallObjectToQueryStringAsyncMethod { get; } + + static ClientProxyUrlBuilder() + { + CallObjectToQueryStringAsyncMethod = typeof(ClientProxyUrlBuilder) + .GetMethods(BindingFlags.NonPublic | BindingFlags.Instance) + .First(m => m.Name == nameof(ObjectToQueryStringAsync) && m.IsGenericMethodDefinition); + } + + protected IServiceScopeFactory ServiceScopeFactory { get; } + + public ClientProxyUrlBuilder(IServiceScopeFactory serviceScopeFactory) + { + ServiceScopeFactory = serviceScopeFactory; + } + + public async Task GenerateUrlWithParametersAsync(ActionApiDescriptionModel action, IReadOnlyDictionary methodArguments, ApiVersionInfo apiVersion) { // The ASP.NET Core route value provider and query string value provider: // Treat values as invariant culture. @@ -24,14 +43,14 @@ namespace Volo.Abp.Http.Client.ClientProxying { var urlBuilder = new StringBuilder(action.Url); - ReplacePathVariables(urlBuilder, action.Parameters, methodArguments, apiVersion); - AddQueryStringParameters(urlBuilder, action.Parameters, methodArguments, apiVersion); + await ReplacePathVariablesAsync(urlBuilder, action.Parameters, methodArguments, apiVersion); + await AddQueryStringParametersAsync(urlBuilder, action.Parameters, methodArguments, apiVersion); return urlBuilder.ToString(); } } - protected virtual void ReplacePathVariables(StringBuilder urlBuilder, IList actionParameters, IReadOnlyDictionary methodArguments, ApiVersionInfo apiVersion) + protected virtual Task ReplacePathVariablesAsync(StringBuilder urlBuilder, IList actionParameters, IReadOnlyDictionary methodArguments, ApiVersionInfo apiVersion) { var pathParameters = actionParameters .Where(p => p.BindingSourceId == ParameterBindingSources.Path) @@ -39,7 +58,7 @@ namespace Volo.Abp.Http.Client.ClientProxying if (!pathParameters.Any()) { - return; + return Task.CompletedTask; } if (pathParameters.Any(p => p.Name == "apiVersion")) @@ -71,9 +90,11 @@ namespace Volo.Abp.Http.Client.ClientProxying urlBuilder = urlBuilder.Replace($"{{{pathParameter.Name}}}", value.ToString()); } } + + return Task.CompletedTask; } - protected virtual void AddQueryStringParameters(StringBuilder urlBuilder, IList actionParameters, IReadOnlyDictionary methodArguments, ApiVersionInfo apiVersion) + protected virtual async Task AddQueryStringParametersAsync(StringBuilder urlBuilder, IList actionParameters, IReadOnlyDictionary methodArguments, ApiVersionInfo apiVersion) { var queryStringParameters = actionParameters .Where(p => p.BindingSourceId.IsIn(ParameterBindingSources.ModelBinding, ParameterBindingSources.Query)) @@ -89,7 +110,26 @@ namespace Volo.Abp.Http.Client.ClientProxying continue; } - if (AddQueryStringParameter(urlBuilder, isFirstParam, queryStringParameter.Name, value)) + using (var scope = ServiceScopeFactory.CreateScope()) + { + var objectToQuery = scope.ServiceProvider.GetService(typeof(IObjectToQueryString<>).MakeGenericType(value.GetType())); + if (objectToQuery != null) + { + var queryString = await (Task)CallObjectToQueryStringAsyncMethod + .MakeGenericMethod(value.GetType()) + .Invoke(this, new object[] { value }); + + if (!queryString.IsNullOrWhiteSpace()) + { + urlBuilder.Append(isFirstParam ? "?" : "&"); + urlBuilder.Append(queryString); + isFirstParam = false; + continue; + } + } + } + + if (await AddQueryStringParameterAsync(urlBuilder, isFirstParam, queryStringParameter.Name, value)) { isFirstParam = false; } @@ -97,11 +137,24 @@ namespace Volo.Abp.Http.Client.ClientProxying if (apiVersion.ShouldSendInQueryString()) { - AddQueryStringParameter(urlBuilder, isFirstParam, "api-version", apiVersion.Version); //TODO: Constant! + await AddQueryStringParameterAsync(urlBuilder, isFirstParam, "api-version", apiVersion.Version); //TODO: Constant! + } + } + + protected virtual async Task ObjectToQueryStringAsync(T value) + { + using (var scope = ServiceScopeFactory.CreateScope()) + { + var objectToQueryString = scope.ServiceProvider.GetService>(); + if (objectToQueryString != null) + { + return await objectToQueryString.ConvertAsync(value); + } } + return null; } - protected virtual bool AddQueryStringParameter( + protected virtual async Task AddQueryStringParameterAsync( StringBuilder urlBuilder, bool isFirstParam, string name, @@ -116,7 +169,7 @@ namespace Volo.Abp.Http.Client.ClientProxying { urlBuilder.Append(isFirstParam ? "?" : "&"); } - urlBuilder.Append(name + $"[{index++}]=" + System.Net.WebUtility.UrlEncode(ConvertValueToString(item)) + "&"); + urlBuilder.Append(name + $"[{index++}]=" + System.Net.WebUtility.UrlEncode(await ConvertValueToStringAsync(item)) + "&"); } if (index > 0) @@ -130,18 +183,18 @@ namespace Volo.Abp.Http.Client.ClientProxying } urlBuilder.Append(isFirstParam ? "?" : "&"); - urlBuilder.Append(name + "=" + System.Net.WebUtility.UrlEncode(ConvertValueToString(value))); + urlBuilder.Append(name + "=" + System.Net.WebUtility.UrlEncode(await ConvertValueToStringAsync(value))); return true; } - protected virtual string ConvertValueToString([CanBeNull] object value) + protected virtual Task ConvertValueToStringAsync([CanBeNull] object value) { if (value is DateTime dateTimeValue) { - return dateTimeValue.ToUniversalTime().ToString("O"); + return Task.FromResult(dateTimeValue.ToUniversalTime().ToString("O")); } - return value?.ToString(); + return Task.FromResult(value?.ToString()); } } } diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs index b040c3e3d4..98225cf424 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs @@ -276,5 +276,32 @@ namespace Volo.Abp.Http.DynamicProxying }); result.ShouldBe("123.rtf:File1:application/rtf:1-1.rtf123.rtf:File2:application/rtf2:1-2.rtf789.rtf:File3:application/rtf3:i-789.rtf"); } + + [Fact] + public async Task GetParamsFromQueryAsync() + { + var result = await _peopleAppService.GetParamsFromQueryAsync(new GetParamsFromQueryInput() + { + NameValues = new List() + { + new GetParamsFromQueryInputNameValue() + { + Name = "name1", + Value = "value1" + }, + new GetParamsFromQueryInputNameValue() + { + Name = "name2", + Value = "value2" + } + }, + NameValue = new GetParamsFromQueryInputNameValue() + { + Name = "name3", + Value = "value3" + } + }); + result.ShouldBe("name1-value1:name2-value2:name3-value3"); + } } } diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/GetParamsFromQueryInput.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/GetParamsFromQueryInput.cs new file mode 100644 index 0000000000..764a6390f0 --- /dev/null +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/GetParamsFromQueryInput.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Http; + +namespace Volo.Abp.TestApp.Application.Dto +{ + public class GetParamsFromQueryInput + { + public List NameValues { get; set; } + + public GetParamsFromQueryInputNameValue NameValue { get; set; } + } + + public class GetParamsFromQueryInputNameValue + { + public string Name { get; set; } + + public string Value { get; set; } + } + + [ExposeServices(typeof(IObjectToQueryString>))] + public class TestInputToQueryString : IObjectToQueryString>, ITransientDependency + { + public Task ConvertAsync(List values) + { + if (values.IsNullOrEmpty()) + { + return null; + } + + var sb = new StringBuilder(); + + for (var i = 0; i < values.Count; i++) + { + sb.Append($"NameValues[{i}].Name={values[i].Name}&NameValues[{i}].Value={values[i].Value}&"); + } + + sb.Remove(sb.Length - 1, 1); + return Task.FromResult(sb.ToString()); + } + } +} diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/IPeopleAppService.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/IPeopleAppService.cs index 844e7fd383..ec63c279f2 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/IPeopleAppService.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/IPeopleAppService.cs @@ -32,5 +32,6 @@ namespace Volo.Abp.TestApp.Application Task CreateMultipleFileAsync(CreateMultipleFileInput input); + Task GetParamsFromQueryAsync(GetParamsFromQueryInput input); } } diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs index a3d8764423..f211a9198f 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs @@ -125,5 +125,13 @@ namespace Volo.Abp.TestApp.Application return str; } + + public Task GetParamsFromQueryAsync(GetParamsFromQueryInput input) + { + return Task.FromResult(input.NameValues?.FirstOrDefault()?.Name + "-" + + input.NameValues?.FirstOrDefault()?.Value + ":" + + input.NameValues?.LastOrDefault()?.Name + "-" + input.NameValues?.LastOrDefault()?.Value + ":" + + input.NameValue?.Name + "-" + input.NameValue?.Value); + } } } From 8b1d93a7e500ecfc70e9d65ad276062cc0eac1ba Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 4 Oct 2021 23:01:35 +0800 Subject: [PATCH 2/6] Update ClientProxyUrlBuilder.cs --- .../ClientProxying/ClientProxyUrlBuilder.cs | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs index 1eb07e88cf..0d97e6d348 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs @@ -110,23 +110,16 @@ namespace Volo.Abp.Http.Client.ClientProxying continue; } - using (var scope = ServiceScopeFactory.CreateScope()) + var queryString = await (Task)CallObjectToQueryStringAsyncMethod + .MakeGenericMethod(value.GetType()) + .Invoke(this, new object[] { value }); + + if (!queryString.IsNullOrWhiteSpace()) { - var objectToQuery = scope.ServiceProvider.GetService(typeof(IObjectToQueryString<>).MakeGenericType(value.GetType())); - if (objectToQuery != null) - { - var queryString = await (Task)CallObjectToQueryStringAsyncMethod - .MakeGenericMethod(value.GetType()) - .Invoke(this, new object[] { value }); - - if (!queryString.IsNullOrWhiteSpace()) - { - urlBuilder.Append(isFirstParam ? "?" : "&"); - urlBuilder.Append(queryString); - isFirstParam = false; - continue; - } - } + urlBuilder.Append(isFirstParam ? "?" : "&"); + urlBuilder.Append(queryString); + isFirstParam = false; + continue; } if (await AddQueryStringParameterAsync(urlBuilder, isFirstParam, queryStringParameter.Name, value)) From fe32230aae3e039be95d5cdae940836b64947777 Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 5 Oct 2021 14:40:21 +0800 Subject: [PATCH 3/6] Add `IObjectToFormData`. --- .../Volo/Abp/Http/IObjectToFormData.cs | 11 ++ .../Client/ClientProxying/ClientProxyBase.cs | 24 ++-- .../ClientProxyRequestPayloadBuilder.cs | 135 ++++++++++-------- .../ClientProxying/ClientProxyUrlBuilder.cs | 2 +- .../PersonAppServiceClientProxy_Tests.cs | 37 ++++- .../Volo.Abp.TestApp/Volo.Abp.TestApp.csproj | 2 +- .../Dto/GetParamsFromQueryInput.cs | 44 ------ .../TestApp/Application/Dto/GetParamsInput.cs | 66 +++++++++ .../TestApp/Application/IPeopleAppService.cs | 4 +- .../TestApp/Application/PeopleAppService.cs | 11 +- 10 files changed, 215 insertions(+), 121 deletions(-) create mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/Http/IObjectToFormData.cs delete mode 100644 framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/GetParamsFromQueryInput.cs create mode 100644 framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/GetParamsInput.cs diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Http/IObjectToFormData.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Http/IObjectToFormData.cs new file mode 100644 index 0000000000..166b51239e --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Http/IObjectToFormData.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using System.Net.Http; +using System.Threading.Tasks; + +namespace Volo.Abp.Http +{ + public interface IObjectToFormData + { + Task>> ConvertAsync(TValue value); + } +} diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyBase.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyBase.cs index 68ebc87543..9b47208141 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyBase.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyBase.cs @@ -28,15 +28,15 @@ namespace Volo.Abp.Http.Client.ClientProxying protected IClientProxyApiDescriptionFinder ClientProxyApiDescriptionFinder => LazyServiceProvider.LazyGetRequiredService(); protected ICancellationTokenProvider CancellationTokenProvider => LazyServiceProvider.LazyGetRequiredService(); protected ICorrelationIdProvider CorrelationIdProvider => LazyServiceProvider.LazyGetRequiredService(); - protected ICurrentTenant CurrentTenant => LazyServiceProvider.LazyGetRequiredService(); - protected IOptions AbpCorrelationIdOptions => LazyServiceProvider.LazyGetRequiredService>(); - protected IProxyHttpClientFactory HttpClientFactory => LazyServiceProvider.LazyGetRequiredService(); - protected IRemoteServiceConfigurationProvider RemoteServiceConfigurationProvider => LazyServiceProvider.LazyGetRequiredService(); - protected IOptions ClientOptions => LazyServiceProvider.LazyGetRequiredService>(); - protected IJsonSerializer JsonSerializer => LazyServiceProvider.LazyGetRequiredService(); - protected IRemoteServiceHttpClientAuthenticator ClientAuthenticator => LazyServiceProvider.LazyGetRequiredService(); - protected ClientProxyRequestPayloadBuilder ClientProxyRequestPayloadBuilder => LazyServiceProvider.LazyGetRequiredService(); - protected ClientProxyUrlBuilder ClientProxyUrlBuilder => LazyServiceProvider.LazyGetRequiredService(); + protected ICurrentTenant CurrentTenant => LazyServiceProvider.LazyGetRequiredService(); + protected IOptions AbpCorrelationIdOptions => LazyServiceProvider.LazyGetRequiredService>(); + protected IProxyHttpClientFactory HttpClientFactory => LazyServiceProvider.LazyGetRequiredService(); + protected IRemoteServiceConfigurationProvider RemoteServiceConfigurationProvider => LazyServiceProvider.LazyGetRequiredService(); + protected IOptions ClientOptions => LazyServiceProvider.LazyGetRequiredService>(); + protected IJsonSerializer JsonSerializer => LazyServiceProvider.LazyGetRequiredService(); + protected IRemoteServiceHttpClientAuthenticator ClientAuthenticator => LazyServiceProvider.LazyGetRequiredService(); + protected ClientProxyRequestPayloadBuilder ClientProxyRequestPayloadBuilder => LazyServiceProvider.LazyGetRequiredService(); + protected ClientProxyUrlBuilder ClientProxyUrlBuilder => LazyServiceProvider.LazyGetRequiredService(); protected virtual async Task RequestAsync(string methodName, ClientProxyRequestTypeValue arguments = null) { @@ -114,7 +114,7 @@ namespace Volo.Abp.Http.Client.ClientProxying var requestMessage = new HttpRequestMessage(requestContext.Action.GetHttpMethod(), url) { - Content = ClientProxyRequestPayloadBuilder.BuildContent(requestContext.Action, requestContext.Arguments, JsonSerializer, apiVersion) + Content = await ClientProxyRequestPayloadBuilder.BuildContentAsync(requestContext.Action, requestContext.Arguments, JsonSerializer, apiVersion) }; AddHeaders(requestContext.Arguments, requestContext.Action, requestMessage, apiVersion); @@ -161,9 +161,9 @@ namespace Volo.Abp.Http.Client.ClientProxying return await ClientProxyUrlBuilder.GenerateUrlWithParametersAsync(requestContext.Action, requestContext.Arguments, apiVersion); } - protected virtual Task GetHttpContentAsync(ClientProxyRequestContext requestContext, ApiVersionInfo apiVersion) + protected virtual async Task GetHttpContentAsync(ClientProxyRequestContext requestContext, ApiVersionInfo apiVersion) { - return Task.FromResult(ClientProxyRequestPayloadBuilder.BuildContent(requestContext.Action, requestContext.Arguments, JsonSerializer, apiVersion)); + return await ClientProxyRequestPayloadBuilder.BuildContentAsync(requestContext.Action, requestContext.Arguments, JsonSerializer, apiVersion); } protected virtual async Task FindBestApiVersionAsync(ClientProxyRequestContext requestContext) diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyRequestPayloadBuilder.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyRequestPayloadBuilder.cs index 3ba7026c8e..f76afae6a3 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyRequestPayloadBuilder.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyRequestPayloadBuilder.cs @@ -3,8 +3,11 @@ using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; +using System.Reflection; using System.Text; +using System.Threading.Tasks; using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Content; using Volo.Abp.DependencyInjection; using Volo.Abp.Http.Client.Proxying; @@ -16,21 +19,37 @@ namespace Volo.Abp.Http.Client.ClientProxying { public class ClientProxyRequestPayloadBuilder : ITransientDependency { + protected static MethodInfo CallObjectToFormDataAsyncMethod { get; } + + static ClientProxyRequestPayloadBuilder() + { + CallObjectToFormDataAsyncMethod = typeof(ClientProxyRequestPayloadBuilder) + .GetMethods(BindingFlags.NonPublic | BindingFlags.Instance) + .First(m => m.Name == nameof(ObjectToFormDataAsync) && m.IsGenericMethodDefinition); + } + + protected IServiceScopeFactory ServiceScopeFactory { get; } + + public ClientProxyRequestPayloadBuilder(IServiceScopeFactory serviceScopeFactory) + { + ServiceScopeFactory = serviceScopeFactory; + } + [CanBeNull] - public virtual HttpContent BuildContent(ActionApiDescriptionModel action, IReadOnlyDictionary methodArguments, IJsonSerializer jsonSerializer, ApiVersionInfo apiVersion) + public virtual async Task BuildContentAsync(ActionApiDescriptionModel action, IReadOnlyDictionary methodArguments, IJsonSerializer jsonSerializer, ApiVersionInfo apiVersion) { - var body = GenerateBody(action, methodArguments, jsonSerializer); + var body = await GenerateBodyAsync(action, methodArguments, jsonSerializer); if (body != null) { return body; } - body = GenerateFormPostData(action, methodArguments); + body = await GenerateFormPostDataAsync(action, methodArguments); return body; } - protected virtual HttpContent GenerateBody(ActionApiDescriptionModel action, IReadOnlyDictionary methodArguments, IJsonSerializer jsonSerializer) + protected virtual Task GenerateBodyAsync(ActionApiDescriptionModel action, IReadOnlyDictionary methodArguments, IJsonSerializer jsonSerializer) { var parameters = action .Parameters @@ -39,7 +58,7 @@ namespace Volo.Abp.Http.Client.ClientProxying if (parameters.Length <= 0) { - return null; + return Task.FromResult(null); } if (parameters.Length > 1) @@ -52,13 +71,13 @@ namespace Volo.Abp.Http.Client.ClientProxying var value = HttpActionParameterHelper.FindParameterValue(methodArguments, parameters[0]); if (value == null) { - return null; + return Task.FromResult(null); } - return new StringContent(jsonSerializer.Serialize(value), Encoding.UTF8, MimeTypes.Application.Json); + return Task.FromResult(new StringContent(jsonSerializer.Serialize(value), Encoding.UTF8, MimeTypes.Application.Json)); } - protected virtual HttpContent GenerateFormPostData(ActionApiDescriptionModel action, IReadOnlyDictionary methodArguments) + protected virtual async Task GenerateFormPostDataAsync(ActionApiDescriptionModel action, IReadOnlyDictionary methodArguments) { var parameters = action .Parameters @@ -70,71 +89,75 @@ namespace Volo.Abp.Http.Client.ClientProxying return null; } - if (parameters.Any(x => x.BindingSourceId == ParameterBindingSources.FormFile)) + var formData = new MultipartFormDataContent(); + + foreach (var parameter in parameters) { - var formData = new MultipartFormDataContent(); - foreach (var parameter in parameters) + var value = HttpActionParameterHelper.FindParameterValue(methodArguments, parameter); + if (value == null) + { + continue; + } + + var formDataContents = await (Task>>)CallObjectToFormDataAsyncMethod + .MakeGenericMethod(value.GetType()) + .Invoke(this, new object[] { value }); + + if (formDataContents != null) { - var value = HttpActionParameterHelper.FindParameterValue(methodArguments, parameter); - if (value == null) + foreach (var content in formDataContents) { - continue; + formData.Add(content.Value, content.Key); } - if (value is IRemoteStreamContent remoteStreamContent) + continue; + } + + if (value is IRemoteStreamContent remoteStreamContent) + { + var stream = remoteStreamContent.GetStream(); + var streamContent = new StreamContent(stream); + if (!remoteStreamContent.ContentType.IsNullOrWhiteSpace()) { - var stream = remoteStreamContent.GetStream(); - var streamContent = new StreamContent(stream); - if (!remoteStreamContent.ContentType.IsNullOrWhiteSpace()) - { - streamContent.Headers.ContentType = new MediaTypeHeaderValue(remoteStreamContent.ContentType); - } - streamContent.Headers.ContentLength = remoteStreamContent.ContentLength; - formData.Add(streamContent, parameter.Name, remoteStreamContent.FileName ?? parameter.Name); + streamContent.Headers.ContentType = new MediaTypeHeaderValue(remoteStreamContent.ContentType); } - else if (value is IEnumerable remoteStreamContents) + streamContent.Headers.ContentLength = remoteStreamContent.ContentLength; + formData.Add(streamContent, parameter.Name, remoteStreamContent.FileName ?? parameter.Name); + } + else if (value is IEnumerable remoteStreamContents) + { + foreach (var content in remoteStreamContents) { - foreach (var content in remoteStreamContents) + var stream = content.GetStream(); + var streamContent = new StreamContent(stream); + if (!content.ContentType.IsNullOrWhiteSpace()) { - var stream = content.GetStream(); - var streamContent = new StreamContent(stream); - if (!content.ContentType.IsNullOrWhiteSpace()) - { - streamContent.Headers.ContentType = new MediaTypeHeaderValue(content.ContentType); - } - streamContent.Headers.ContentLength = content.ContentLength; - formData.Add(streamContent, parameter.Name, content.FileName ?? parameter.Name); + streamContent.Headers.ContentType = new MediaTypeHeaderValue(content.ContentType); } - } - else - { - formData.Add(new StringContent(value.ToString(), Encoding.UTF8), parameter.Name); + streamContent.Headers.ContentLength = content.ContentLength; + formData.Add(streamContent, parameter.Name, content.FileName ?? parameter.Name); } } - - return formData; - } - else - { - var postDataBuilder = new StringBuilder(); - - var isFirstParam = true; - foreach (var parameter in parameters.Where(p => p.BindingSourceId == ParameterBindingSources.Form)) + else { - var value = HttpActionParameterHelper.FindParameterValue(methodArguments, parameter); - if (value == null) - { - continue; - } + formData.Add(new StringContent(value.ToString(), Encoding.UTF8), parameter.Name); + } + } - postDataBuilder.Append(isFirstParam ? "?" : "&"); - postDataBuilder.Append(parameter.Name + "=" + System.Net.WebUtility.UrlEncode(value.ToString())); + return formData; + } - isFirstParam = false; + protected virtual async Task>> ObjectToFormDataAsync(T value) + { + using (var scope = ServiceScopeFactory.CreateScope()) + { + var objectToFormData = scope.ServiceProvider.GetService>(); + if (objectToFormData != null) + { + return await objectToFormData.ConvertAsync(value); } - - return new StringContent(postDataBuilder.ToString(), Encoding.UTF8, MimeTypes.Application.XWwwFormUrlencoded); } + return null; } } } diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs index 0d97e6d348..97b9d747cf 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs @@ -114,7 +114,7 @@ namespace Volo.Abp.Http.Client.ClientProxying .MakeGenericMethod(value.GetType()) .Invoke(this, new object[] { value }); - if (!queryString.IsNullOrWhiteSpace()) + if (queryString != null) { urlBuilder.Append(isFirstParam ? "?" : "&"); urlBuilder.Append(queryString); diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs index 98225cf424..789c552861 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs @@ -280,22 +280,49 @@ namespace Volo.Abp.Http.DynamicProxying [Fact] public async Task GetParamsFromQueryAsync() { - var result = await _peopleAppService.GetParamsFromQueryAsync(new GetParamsFromQueryInput() + var result = await _peopleAppService.GetParamsFromQueryAsync(new GetParamsInput() { - NameValues = new List() + NameValues = new List() { - new GetParamsFromQueryInputNameValue() + new GetParamsNameValue() { Name = "name1", Value = "value1" }, - new GetParamsFromQueryInputNameValue() + new GetParamsNameValue() { Name = "name2", Value = "value2" } }, - NameValue = new GetParamsFromQueryInputNameValue() + NameValue = new GetParamsNameValue() + { + Name = "name3", + Value = "value3" + } + }); + result.ShouldBe("name1-value1:name2-value2:name3-value3"); + } + + [Fact] + public async Task GetParamsFromFormAsync() + { + var result = await _peopleAppService.GetParamsFromFormAsync(new GetParamsInput() + { + NameValues = new List() + { + new GetParamsNameValue() + { + Name = "name1", + Value = "value1" + }, + new GetParamsNameValue() + { + Name = "name2", + Value = "value2" + } + }, + NameValue = new GetParamsNameValue() { Name = "name3", Value = "value3" diff --git a/framework/test/Volo.Abp.TestApp/Volo.Abp.TestApp.csproj b/framework/test/Volo.Abp.TestApp/Volo.Abp.TestApp.csproj index 6dc99c23c3..0664ad2b7b 100644 --- a/framework/test/Volo.Abp.TestApp/Volo.Abp.TestApp.csproj +++ b/framework/test/Volo.Abp.TestApp/Volo.Abp.TestApp.csproj @@ -1,4 +1,4 @@ - + diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/GetParamsFromQueryInput.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/GetParamsFromQueryInput.cs deleted file mode 100644 index 764a6390f0..0000000000 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/GetParamsFromQueryInput.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; -using Volo.Abp.DependencyInjection; -using Volo.Abp.Http; - -namespace Volo.Abp.TestApp.Application.Dto -{ - public class GetParamsFromQueryInput - { - public List NameValues { get; set; } - - public GetParamsFromQueryInputNameValue NameValue { get; set; } - } - - public class GetParamsFromQueryInputNameValue - { - public string Name { get; set; } - - public string Value { get; set; } - } - - [ExposeServices(typeof(IObjectToQueryString>))] - public class TestInputToQueryString : IObjectToQueryString>, ITransientDependency - { - public Task ConvertAsync(List values) - { - if (values.IsNullOrEmpty()) - { - return null; - } - - var sb = new StringBuilder(); - - for (var i = 0; i < values.Count; i++) - { - sb.Append($"NameValues[{i}].Name={values[i].Name}&NameValues[{i}].Value={values[i].Value}&"); - } - - sb.Remove(sb.Length - 1, 1); - return Task.FromResult(sb.ToString()); - } - } -} diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/GetParamsInput.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/GetParamsInput.cs new file mode 100644 index 0000000000..3cbb1d4794 --- /dev/null +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/GetParamsInput.cs @@ -0,0 +1,66 @@ +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Http; + +namespace Volo.Abp.TestApp.Application.Dto +{ + public class GetParamsInput + { + public List NameValues { get; set; } + + public GetParamsNameValue NameValue { get; set; } + } + + public class GetParamsNameValue + { + public string Name { get; set; } + + public string Value { get; set; } + } + + [ExposeServices(typeof(IObjectToQueryString>))] + public class TestObjectToQueryString : IObjectToQueryString>, ITransientDependency + { + public Task ConvertAsync(List values) + { + if (values.IsNullOrEmpty()) + { + return null; + } + + var sb = new StringBuilder(); + + for (var i = 0; i < values.Count; i++) + { + sb.Append($"NameValues[{i}].Name={values[i].Name}&NameValues[{i}].Value={values[i].Value}&"); + } + + sb.Remove(sb.Length - 1, 1); + return Task.FromResult(sb.ToString()); + } + } + + [ExposeServices(typeof(IObjectToFormData>))] + public class TestObjectToFormData : IObjectToFormData>, ITransientDependency + { + public Task>> ConvertAsync(List values) + { + if (values.IsNullOrEmpty()) + { + return null; + } + + var formDataContents = new List>(); + for (var i = 0; i < values.Count; i++) + { + formDataContents.Add(new KeyValuePair($"NameValues[{i}].Name", new StringContent(values[i].Name, Encoding.UTF8))); + formDataContents.Add(new KeyValuePair($"NameValues[{i}].Value", new StringContent(values[i].Value, Encoding.UTF8))); + } + + return Task.FromResult(formDataContents); + } + } +} diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/IPeopleAppService.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/IPeopleAppService.cs index ec63c279f2..b9cb2e0887 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/IPeopleAppService.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/IPeopleAppService.cs @@ -32,6 +32,8 @@ namespace Volo.Abp.TestApp.Application Task CreateMultipleFileAsync(CreateMultipleFileInput input); - Task GetParamsFromQueryAsync(GetParamsFromQueryInput input); + Task GetParamsFromQueryAsync(GetParamsInput input); + + Task GetParamsFromFormAsync(GetParamsInput input); } } diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs index f211a9198f..4baa2a4ec2 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; using Volo.Abp.Application.Dtos; using Volo.Abp.TestApp.Domain; using Volo.Abp.Domain.Repositories; @@ -126,7 +127,15 @@ namespace Volo.Abp.TestApp.Application return str; } - public Task GetParamsFromQueryAsync(GetParamsFromQueryInput input) + public Task GetParamsFromQueryAsync([FromQuery]GetParamsInput input) + { + return Task.FromResult(input.NameValues?.FirstOrDefault()?.Name + "-" + + input.NameValues?.FirstOrDefault()?.Value + ":" + + input.NameValues?.LastOrDefault()?.Name + "-" + input.NameValues?.LastOrDefault()?.Value + ":" + + input.NameValue?.Name + "-" + input.NameValue?.Value); + } + + public Task GetParamsFromFormAsync([FromForm]GetParamsInput input) { return Task.FromResult(input.NameValues?.FirstOrDefault()?.Name + "-" + input.NameValues?.FirstOrDefault()?.Value + ":" + From 4551acda13776c481467723e96cff3261b6b144d Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 6 Oct 2021 11:09:26 +0800 Subject: [PATCH 4/6] Move interface to `Http.Client`. --- .../ClientProxying}/IObjectToFormData.cs | 2 +- .../ClientProxying}/IObjectToQueryString.cs | 2 +- .../DynamicProxying/TestObjectToFormData.cs | 31 ++++++++++++ .../TestObjectToQueryString.cs | 31 ++++++++++++ .../TestApp/Application/Dto/GetParamsInput.cs | 48 ------------------- 5 files changed, 64 insertions(+), 50 deletions(-) rename framework/src/{Volo.Abp.Core/Volo/Abp/Http => Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying}/IObjectToFormData.cs (83%) rename framework/src/{Volo.Abp.Core/Volo/Abp/Http => Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying}/IObjectToQueryString.cs (76%) create mode 100644 framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToFormData.cs create mode 100644 framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToQueryString.cs diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Http/IObjectToFormData.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/IObjectToFormData.cs similarity index 83% rename from framework/src/Volo.Abp.Core/Volo/Abp/Http/IObjectToFormData.cs rename to framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/IObjectToFormData.cs index 166b51239e..5aec4252e3 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Http/IObjectToFormData.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/IObjectToFormData.cs @@ -2,7 +2,7 @@ using System.Net.Http; using System.Threading.Tasks; -namespace Volo.Abp.Http +namespace Volo.Abp.Http.Client.ClientProxying { public interface IObjectToFormData { diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Http/IObjectToQueryString.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/IObjectToQueryString.cs similarity index 76% rename from framework/src/Volo.Abp.Core/Volo/Abp/Http/IObjectToQueryString.cs rename to framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/IObjectToQueryString.cs index 19696e546c..ef223a903d 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Http/IObjectToQueryString.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/IObjectToQueryString.cs @@ -1,6 +1,6 @@ using System.Threading.Tasks; -namespace Volo.Abp.Http +namespace Volo.Abp.Http.Client.ClientProxying { public interface IObjectToQueryString { diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToFormData.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToFormData.cs new file mode 100644 index 0000000000..a82923c0ac --- /dev/null +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToFormData.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Http.Client.ClientProxying; +using Volo.Abp.TestApp.Application.Dto; + +namespace Volo.Abp.Http.DynamicProxying +{ + [ExposeServices(typeof(IObjectToFormData>))] + public class TestObjectToFormData : IObjectToFormData>, ITransientDependency + { + public Task>> ConvertAsync(List values) + { + if (values.IsNullOrEmpty()) + { + return null; + } + + var formDataContents = new List>(); + for (var i = 0; i < values.Count; i++) + { + formDataContents.Add(new KeyValuePair($"NameValues[{i}].Name", new StringContent(values[i].Name, Encoding.UTF8))); + formDataContents.Add(new KeyValuePair($"NameValues[{i}].Value", new StringContent(values[i].Value, Encoding.UTF8))); + } + + return Task.FromResult(formDataContents); + } + } +} diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToQueryString.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToQueryString.cs new file mode 100644 index 0000000000..13c470666a --- /dev/null +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToQueryString.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Http.Client.ClientProxying; +using Volo.Abp.TestApp.Application.Dto; + +namespace Volo.Abp.Http.DynamicProxying +{ + [ExposeServices(typeof(IObjectToQueryString>))] + public class TestObjectToQueryString : IObjectToQueryString>, ITransientDependency + { + public Task ConvertAsync(List values) + { + if (values.IsNullOrEmpty()) + { + return null; + } + + var sb = new StringBuilder(); + + for (var i = 0; i < values.Count; i++) + { + sb.Append($"NameValues[{i}].Name={values[i].Name}&NameValues[{i}].Value={values[i].Value}&"); + } + + sb.Remove(sb.Length - 1, 1); + return Task.FromResult(sb.ToString()); + } + } +} diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/GetParamsInput.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/GetParamsInput.cs index 3cbb1d4794..93a643491c 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/GetParamsInput.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/GetParamsInput.cs @@ -1,9 +1,4 @@ using System.Collections.Generic; -using System.Net.Http; -using System.Text; -using System.Threading.Tasks; -using Volo.Abp.DependencyInjection; -using Volo.Abp.Http; namespace Volo.Abp.TestApp.Application.Dto { @@ -20,47 +15,4 @@ namespace Volo.Abp.TestApp.Application.Dto public string Value { get; set; } } - - [ExposeServices(typeof(IObjectToQueryString>))] - public class TestObjectToQueryString : IObjectToQueryString>, ITransientDependency - { - public Task ConvertAsync(List values) - { - if (values.IsNullOrEmpty()) - { - return null; - } - - var sb = new StringBuilder(); - - for (var i = 0; i < values.Count; i++) - { - sb.Append($"NameValues[{i}].Name={values[i].Name}&NameValues[{i}].Value={values[i].Value}&"); - } - - sb.Remove(sb.Length - 1, 1); - return Task.FromResult(sb.ToString()); - } - } - - [ExposeServices(typeof(IObjectToFormData>))] - public class TestObjectToFormData : IObjectToFormData>, ITransientDependency - { - public Task>> ConvertAsync(List values) - { - if (values.IsNullOrEmpty()) - { - return null; - } - - var formDataContents = new List>(); - for (var i = 0; i < values.Count; i++) - { - formDataContents.Add(new KeyValuePair($"NameValues[{i}].Name", new StringContent(values[i].Name, Encoding.UTF8))); - formDataContents.Add(new KeyValuePair($"NameValues[{i}].Value", new StringContent(values[i].Value, Encoding.UTF8))); - } - - return Task.FromResult(formDataContents); - } - } } From 6808effa17fb09ae8dc090bb94af746a9945d0c9 Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 6 Oct 2021 11:53:19 +0800 Subject: [PATCH 5/6] Add `AbpHttpClientProxyingOptions` --- .../AbpHttpClientProxyingOptions.cs | 18 ++++++++ .../ClientProxyRequestPayloadBuilder.cs | 46 +++++++++++-------- .../ClientProxying/ClientProxyUrlBuilder.cs | 46 +++++++++++-------- .../Volo/Abp/Http/AbpHttpClientTestModule.cs | 11 ++++- 4 files changed, 80 insertions(+), 41 deletions(-) create mode 100644 framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/AbpHttpClientProxyingOptions.cs diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/AbpHttpClientProxyingOptions.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/AbpHttpClientProxyingOptions.cs new file mode 100644 index 0000000000..87ee7de37c --- /dev/null +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/AbpHttpClientProxyingOptions.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; + +namespace Volo.Abp.Http.Client.ClientProxying +{ + public class AbpHttpClientProxyingOptions + { + public Dictionary QueryStringConverts { get; set; } + + public Dictionary FormDataConverts { get; set; } + + public AbpHttpClientProxyingOptions() + { + QueryStringConverts = new Dictionary(); + FormDataConverts = new Dictionary(); + } + } +} diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyRequestPayloadBuilder.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyRequestPayloadBuilder.cs index f76afae6a3..cee57d7663 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyRequestPayloadBuilder.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyRequestPayloadBuilder.cs @@ -8,6 +8,7 @@ using System.Text; using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using Volo.Abp.Content; using Volo.Abp.DependencyInjection; using Volo.Abp.Http.Client.Proxying; @@ -30,9 +31,12 @@ namespace Volo.Abp.Http.Client.ClientProxying protected IServiceScopeFactory ServiceScopeFactory { get; } - public ClientProxyRequestPayloadBuilder(IServiceScopeFactory serviceScopeFactory) + protected AbpHttpClientProxyingOptions HttpClientProxyingOptions { get; } + + public ClientProxyRequestPayloadBuilder(IServiceScopeFactory serviceScopeFactory, IOptions httpClientProxyingOptions) { ServiceScopeFactory = serviceScopeFactory; + HttpClientProxyingOptions = httpClientProxyingOptions.Value; } [CanBeNull] @@ -99,18 +103,28 @@ namespace Volo.Abp.Http.Client.ClientProxying continue; } - var formDataContents = await (Task>>)CallObjectToFormDataAsyncMethod - .MakeGenericMethod(value.GetType()) - .Invoke(this, new object[] { value }); - - if (formDataContents != null) + if (HttpClientProxyingOptions.FormDataConverts.ContainsKey(value.GetType())) { - foreach (var content in formDataContents) + using (var scope = ServiceScopeFactory.CreateScope()) { - formData.Add(content.Value, content.Key); + var formDataContents = await (Task>>)CallObjectToFormDataAsyncMethod + .MakeGenericMethod(value.GetType()) + .Invoke(this, new object[] + { + scope.ServiceProvider.GetRequiredService( + typeof(IObjectToFormData<>).MakeGenericType(value.GetType())), + value + }); + + if (formDataContents != null) + { + foreach (var content in formDataContents) + { + formData.Add(content.Value, content.Key); + } + continue; + } } - - continue; } if (value is IRemoteStreamContent remoteStreamContent) @@ -147,17 +161,9 @@ namespace Volo.Abp.Http.Client.ClientProxying return formData; } - protected virtual async Task>> ObjectToFormDataAsync(T value) + protected virtual async Task>> ObjectToFormDataAsync(IObjectToFormData converter, T value) { - using (var scope = ServiceScopeFactory.CreateScope()) - { - var objectToFormData = scope.ServiceProvider.GetService>(); - if (objectToFormData != null) - { - return await objectToFormData.ConvertAsync(value); - } - } - return null; + return await converter.ConvertAsync(value); } } } diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs index 97b9d747cf..e59cac6911 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs @@ -8,6 +8,7 @@ using System.Text; using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; using Volo.Abp.Http.Client.Proxying; using Volo.Abp.Http.Modeling; @@ -28,10 +29,12 @@ namespace Volo.Abp.Http.Client.ClientProxying } protected IServiceScopeFactory ServiceScopeFactory { get; } + protected AbpHttpClientProxyingOptions HttpClientProxyingOptions { get; } - public ClientProxyUrlBuilder(IServiceScopeFactory serviceScopeFactory) + public ClientProxyUrlBuilder(IServiceScopeFactory serviceScopeFactory, IOptions httpClientProxyingOptions) { ServiceScopeFactory = serviceScopeFactory; + HttpClientProxyingOptions = httpClientProxyingOptions.Value; } public async Task GenerateUrlWithParametersAsync(ActionApiDescriptionModel action, IReadOnlyDictionary methodArguments, ApiVersionInfo apiVersion) @@ -110,16 +113,27 @@ namespace Volo.Abp.Http.Client.ClientProxying continue; } - var queryString = await (Task)CallObjectToQueryStringAsyncMethod - .MakeGenericMethod(value.GetType()) - .Invoke(this, new object[] { value }); - - if (queryString != null) + if (HttpClientProxyingOptions.QueryStringConverts.ContainsKey(value.GetType())) { - urlBuilder.Append(isFirstParam ? "?" : "&"); - urlBuilder.Append(queryString); - isFirstParam = false; - continue; + using (var scope = ServiceScopeFactory.CreateScope()) + { + var queryString = await (Task)CallObjectToQueryStringAsyncMethod + .MakeGenericMethod(value.GetType()) + .Invoke(this, new object[] + { + scope.ServiceProvider.GetRequiredService( + typeof(IObjectToQueryString<>).MakeGenericType(value.GetType())), + value + }); + + if (queryString != null) + { + urlBuilder.Append(isFirstParam ? "?" : "&"); + urlBuilder.Append(queryString); + isFirstParam = false; + continue; + } + } } if (await AddQueryStringParameterAsync(urlBuilder, isFirstParam, queryStringParameter.Name, value)) @@ -134,17 +148,9 @@ namespace Volo.Abp.Http.Client.ClientProxying } } - protected virtual async Task ObjectToQueryStringAsync(T value) + protected virtual async Task ObjectToQueryStringAsync(IObjectToQueryString converter, T value) { - using (var scope = ServiceScopeFactory.CreateScope()) - { - var objectToQueryString = scope.ServiceProvider.GetService>(); - if (objectToQueryString != null) - { - return await objectToQueryString.ConvertAsync(value); - } - } - return null; + return await converter.ConvertAsync(value); } protected virtual async Task AddQueryStringParameterAsync( diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpClientTestModule.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpClientTestModule.cs index 966ab01840..85713929cf 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpClientTestModule.cs +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpClientTestModule.cs @@ -1,7 +1,9 @@ -using Microsoft.Extensions.DependencyInjection; +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.AspNetCore.Mvc.Conventions; using Volo.Abp.Http.Client; +using Volo.Abp.Http.Client.ClientProxying; using Volo.Abp.Http.DynamicProxying; using Volo.Abp.Http.Localization; using Volo.Abp.Localization; @@ -52,6 +54,13 @@ namespace Volo.Abp.Http options.ConventionalControllers.FormBodyBindingIgnoredTypes.Add(typeof(CreateFileInput)); options.ConventionalControllers.FormBodyBindingIgnoredTypes.Add(typeof(CreateMultipleFileInput)); }); + + + Configure(options => + { + options.QueryStringConverts.Add(typeof(List), typeof(TestObjectToQueryString)); + options.FormDataConverts.Add(typeof(List), typeof(TestObjectToFormData)); + }); } } } From ac9e24fbc605970a65113ed92971e282ff6f8fea Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 6 Oct 2021 12:05:41 +0800 Subject: [PATCH 6/6] Use type to resolve service. --- .../Client/ClientProxying/ClientProxyRequestPayloadBuilder.cs | 3 +-- .../Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs | 3 +-- .../Volo/Abp/Http/AbpHttpClientTestModule.cs | 1 - .../Volo/Abp/Http/DynamicProxying/TestObjectToFormData.cs | 1 - .../Volo/Abp/Http/DynamicProxying/TestObjectToQueryString.cs | 1 - 5 files changed, 2 insertions(+), 7 deletions(-) diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyRequestPayloadBuilder.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyRequestPayloadBuilder.cs index cee57d7663..a91a9904de 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyRequestPayloadBuilder.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyRequestPayloadBuilder.cs @@ -111,8 +111,7 @@ namespace Volo.Abp.Http.Client.ClientProxying .MakeGenericMethod(value.GetType()) .Invoke(this, new object[] { - scope.ServiceProvider.GetRequiredService( - typeof(IObjectToFormData<>).MakeGenericType(value.GetType())), + scope.ServiceProvider.GetRequiredService(HttpClientProxyingOptions.FormDataConverts[value.GetType()]), value }); diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs index e59cac6911..12f7bc1ac8 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyUrlBuilder.cs @@ -121,8 +121,7 @@ namespace Volo.Abp.Http.Client.ClientProxying .MakeGenericMethod(value.GetType()) .Invoke(this, new object[] { - scope.ServiceProvider.GetRequiredService( - typeof(IObjectToQueryString<>).MakeGenericType(value.GetType())), + scope.ServiceProvider.GetRequiredService(HttpClientProxyingOptions.QueryStringConverts[value.GetType()]), value }); diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpClientTestModule.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpClientTestModule.cs index 85713929cf..d90ef50006 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpClientTestModule.cs +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpClientTestModule.cs @@ -55,7 +55,6 @@ namespace Volo.Abp.Http options.ConventionalControllers.FormBodyBindingIgnoredTypes.Add(typeof(CreateMultipleFileInput)); }); - Configure(options => { options.QueryStringConverts.Add(typeof(List), typeof(TestObjectToQueryString)); diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToFormData.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToFormData.cs index a82923c0ac..65b65fc54b 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToFormData.cs +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToFormData.cs @@ -8,7 +8,6 @@ using Volo.Abp.TestApp.Application.Dto; namespace Volo.Abp.Http.DynamicProxying { - [ExposeServices(typeof(IObjectToFormData>))] public class TestObjectToFormData : IObjectToFormData>, ITransientDependency { public Task>> ConvertAsync(List values) diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToQueryString.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToQueryString.cs index 13c470666a..ae11a7bf2f 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToQueryString.cs +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/TestObjectToQueryString.cs @@ -7,7 +7,6 @@ using Volo.Abp.TestApp.Application.Dto; namespace Volo.Abp.Http.DynamicProxying { - [ExposeServices(typeof(IObjectToQueryString>))] public class TestObjectToQueryString : IObjectToQueryString>, ITransientDependency { public Task ConvertAsync(List values)