Add `IObjectToFormData`.

pull/10220/head
maliming 4 years ago
parent 8b1d93a7e5
commit fe32230aae

@ -0,0 +1,11 @@
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
namespace Volo.Abp.Http
{
public interface IObjectToFormData<in TValue>
{
Task<List<KeyValuePair<string, HttpContent>>> ConvertAsync(TValue value);
}
}

@ -28,15 +28,15 @@ namespace Volo.Abp.Http.Client.ClientProxying
protected IClientProxyApiDescriptionFinder ClientProxyApiDescriptionFinder => LazyServiceProvider.LazyGetRequiredService<IClientProxyApiDescriptionFinder>();
protected ICancellationTokenProvider CancellationTokenProvider => LazyServiceProvider.LazyGetRequiredService<ICancellationTokenProvider>();
protected ICorrelationIdProvider CorrelationIdProvider => LazyServiceProvider.LazyGetRequiredService<ICorrelationIdProvider>();
protected ICurrentTenant CurrentTenant => LazyServiceProvider.LazyGetRequiredService<ICurrentTenant>();
protected IOptions<AbpCorrelationIdOptions> AbpCorrelationIdOptions => LazyServiceProvider.LazyGetRequiredService<IOptions<AbpCorrelationIdOptions>>();
protected IProxyHttpClientFactory HttpClientFactory => LazyServiceProvider.LazyGetRequiredService<IProxyHttpClientFactory>();
protected IRemoteServiceConfigurationProvider RemoteServiceConfigurationProvider => LazyServiceProvider.LazyGetRequiredService<IRemoteServiceConfigurationProvider>();
protected IOptions<AbpHttpClientOptions> ClientOptions => LazyServiceProvider.LazyGetRequiredService<IOptions<AbpHttpClientOptions>>();
protected IJsonSerializer JsonSerializer => LazyServiceProvider.LazyGetRequiredService<IJsonSerializer>();
protected IRemoteServiceHttpClientAuthenticator ClientAuthenticator => LazyServiceProvider.LazyGetRequiredService<IRemoteServiceHttpClientAuthenticator>();
protected ClientProxyRequestPayloadBuilder ClientProxyRequestPayloadBuilder => LazyServiceProvider.LazyGetRequiredService<ClientProxyRequestPayloadBuilder>();
protected ClientProxyUrlBuilder ClientProxyUrlBuilder => LazyServiceProvider.LazyGetRequiredService<ClientProxyUrlBuilder>();
protected ICurrentTenant CurrentTenant => LazyServiceProvider.LazyGetRequiredService<ICurrentTenant>();
protected IOptions<AbpCorrelationIdOptions> AbpCorrelationIdOptions => LazyServiceProvider.LazyGetRequiredService<IOptions<AbpCorrelationIdOptions>>();
protected IProxyHttpClientFactory HttpClientFactory => LazyServiceProvider.LazyGetRequiredService<IProxyHttpClientFactory>();
protected IRemoteServiceConfigurationProvider RemoteServiceConfigurationProvider => LazyServiceProvider.LazyGetRequiredService<IRemoteServiceConfigurationProvider>();
protected IOptions<AbpHttpClientOptions> ClientOptions => LazyServiceProvider.LazyGetRequiredService<IOptions<AbpHttpClientOptions>>();
protected IJsonSerializer JsonSerializer => LazyServiceProvider.LazyGetRequiredService<IJsonSerializer>();
protected IRemoteServiceHttpClientAuthenticator ClientAuthenticator => LazyServiceProvider.LazyGetRequiredService<IRemoteServiceHttpClientAuthenticator>();
protected ClientProxyRequestPayloadBuilder ClientProxyRequestPayloadBuilder => LazyServiceProvider.LazyGetRequiredService<ClientProxyRequestPayloadBuilder>();
protected ClientProxyUrlBuilder ClientProxyUrlBuilder => LazyServiceProvider.LazyGetRequiredService<ClientProxyUrlBuilder>();
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<HttpContent> GetHttpContentAsync(ClientProxyRequestContext requestContext, ApiVersionInfo apiVersion)
protected virtual async Task<HttpContent> 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<string> FindBestApiVersionAsync(ClientProxyRequestContext requestContext)

@ -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<string, object> methodArguments, IJsonSerializer jsonSerializer, ApiVersionInfo apiVersion)
public virtual async Task<HttpContent> BuildContentAsync(ActionApiDescriptionModel action, IReadOnlyDictionary<string, object> 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<string, object> methodArguments, IJsonSerializer jsonSerializer)
protected virtual Task<HttpContent> GenerateBodyAsync(ActionApiDescriptionModel action, IReadOnlyDictionary<string, object> 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<HttpContent>(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<HttpContent>(null);
}
return new StringContent(jsonSerializer.Serialize(value), Encoding.UTF8, MimeTypes.Application.Json);
return Task.FromResult<HttpContent>(new StringContent(jsonSerializer.Serialize(value), Encoding.UTF8, MimeTypes.Application.Json));
}
protected virtual HttpContent GenerateFormPostData(ActionApiDescriptionModel action, IReadOnlyDictionary<string, object> methodArguments)
protected virtual async Task<HttpContent> GenerateFormPostDataAsync(ActionApiDescriptionModel action, IReadOnlyDictionary<string, object> 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<List<KeyValuePair<string, HttpContent>>>)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<IRemoteStreamContent> remoteStreamContents)
streamContent.Headers.ContentLength = remoteStreamContent.ContentLength;
formData.Add(streamContent, parameter.Name, remoteStreamContent.FileName ?? parameter.Name);
}
else if (value is IEnumerable<IRemoteStreamContent> 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<List<KeyValuePair<string, HttpContent>>> ObjectToFormDataAsync<T>(T value)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var objectToFormData = scope.ServiceProvider.GetService<IObjectToFormData<T>>();
if (objectToFormData != null)
{
return await objectToFormData.ConvertAsync(value);
}
return new StringContent(postDataBuilder.ToString(), Encoding.UTF8, MimeTypes.Application.XWwwFormUrlencoded);
}
return null;
}
}
}

@ -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);

@ -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<GetParamsFromQueryInputNameValue>()
NameValues = new List<GetParamsNameValue>()
{
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<GetParamsNameValue>()
{
new GetParamsNameValue()
{
Name = "name1",
Value = "value1"
},
new GetParamsNameValue()
{
Name = "name2",
Value = "value2"
}
},
NameValue = new GetParamsNameValue()
{
Name = "name3",
Value = "value3"

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="..\..\..\common.test.props" />

@ -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<GetParamsFromQueryInputNameValue> NameValues { get; set; }
public GetParamsFromQueryInputNameValue NameValue { get; set; }
}
public class GetParamsFromQueryInputNameValue
{
public string Name { get; set; }
public string Value { get; set; }
}
[ExposeServices(typeof(IObjectToQueryString<List<GetParamsFromQueryInputNameValue>>))]
public class TestInputToQueryString : IObjectToQueryString<List<GetParamsFromQueryInputNameValue>>, ITransientDependency
{
public Task<string> ConvertAsync(List<GetParamsFromQueryInputNameValue> 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());
}
}
}

@ -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<GetParamsNameValue> NameValues { get; set; }
public GetParamsNameValue NameValue { get; set; }
}
public class GetParamsNameValue
{
public string Name { get; set; }
public string Value { get; set; }
}
[ExposeServices(typeof(IObjectToQueryString<List<GetParamsNameValue>>))]
public class TestObjectToQueryString : IObjectToQueryString<List<GetParamsNameValue>>, ITransientDependency
{
public Task<string> ConvertAsync(List<GetParamsNameValue> 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<List<GetParamsNameValue>>))]
public class TestObjectToFormData : IObjectToFormData<List<GetParamsNameValue>>, ITransientDependency
{
public Task<List<KeyValuePair<string, HttpContent>>> ConvertAsync(List<GetParamsNameValue> values)
{
if (values.IsNullOrEmpty())
{
return null;
}
var formDataContents = new List<KeyValuePair<string, HttpContent>>();
for (var i = 0; i < values.Count; i++)
{
formDataContents.Add(new KeyValuePair<string, HttpContent>($"NameValues[{i}].Name", new StringContent(values[i].Name, Encoding.UTF8)));
formDataContents.Add(new KeyValuePair<string, HttpContent>($"NameValues[{i}].Value", new StringContent(values[i].Value, Encoding.UTF8)));
}
return Task.FromResult(formDataContents);
}
}
}

@ -32,6 +32,8 @@ namespace Volo.Abp.TestApp.Application
Task<string> CreateMultipleFileAsync(CreateMultipleFileInput input);
Task<string> GetParamsFromQueryAsync(GetParamsFromQueryInput input);
Task<string> GetParamsFromQueryAsync(GetParamsInput input);
Task<string> GetParamsFromFormAsync(GetParamsInput input);
}
}

@ -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<string> GetParamsFromQueryAsync(GetParamsFromQueryInput input)
public Task<string> 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<string> GetParamsFromFormAsync([FromForm]GetParamsInput input)
{
return Task.FromResult(input.NameValues?.FirstOrDefault()?.Name + "-" +
input.NameValues?.FirstOrDefault()?.Value + ":" +

Loading…
Cancel
Save