|
|
|
|
@ -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<string, object> 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<string> GenerateUrlWithParametersAsync(ActionApiDescriptionModel action, IReadOnlyDictionary<string, object> 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<ParameterApiDescriptionModel> actionParameters, IReadOnlyDictionary<string, object> methodArguments, ApiVersionInfo apiVersion)
|
|
|
|
|
protected virtual Task ReplacePathVariablesAsync(StringBuilder urlBuilder, IList<ParameterApiDescriptionModel> actionParameters, IReadOnlyDictionary<string, object> 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<ParameterApiDescriptionModel> actionParameters, IReadOnlyDictionary<string, object> methodArguments, ApiVersionInfo apiVersion)
|
|
|
|
|
protected virtual async Task AddQueryStringParametersAsync(StringBuilder urlBuilder, IList<ParameterApiDescriptionModel> actionParameters, IReadOnlyDictionary<string, object> 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<string>)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<string> ObjectToQueryStringAsync<T>(T value)
|
|
|
|
|
{
|
|
|
|
|
using (var scope = ServiceScopeFactory.CreateScope())
|
|
|
|
|
{
|
|
|
|
|
var objectToQueryString = scope.ServiceProvider.GetService<IObjectToQueryString<T>>();
|
|
|
|
|
if (objectToQueryString != null)
|
|
|
|
|
{
|
|
|
|
|
return await objectToQueryString.ConvertAsync(value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected virtual bool AddQueryStringParameter(
|
|
|
|
|
protected virtual async Task<bool> 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<string> 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());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|