Skip to content

Commit

Permalink
feat: determine url substitutions on first call
Browse files Browse the repository at this point in the history
  • Loading branch information
TimothyMakkison committed Jun 25, 2024
1 parent 9435295 commit 39e0db7
Show file tree
Hide file tree
Showing 2 changed files with 214 additions and 161 deletions.
156 changes: 90 additions & 66 deletions Refit/RequestBuilderImplementation.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
using System.Collections;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;

namespace Refit
Expand Down Expand Up @@ -635,9 +635,6 @@ bool paramsContainsCancellationToken
multiPartContent = new MultipartFormDataContent(restMethod.MultipartBoundary);
ret.Content = multiPartContent;
}
var urlTarget =
(basePath == "/" ? string.Empty : basePath) + restMethod.RelativePath;
var queryParamsToAdd = new List<KeyValuePair<string, string?>>();
var headersToAdd = new Dictionary<string, string?>(restMethod.Headers);
var propertiesToAdd = new Dictionary<string, object?>();
Expand All @@ -652,69 +649,8 @@ bool paramsContainsCancellationToken
if (restMethod.ParameterMap.TryGetValue(i, out var parameterMapValue))
{
parameterInfo = parameterMapValue;
if (parameterInfo.IsObjectPropertyParameter)
{
foreach (var propertyInfo in parameterInfo.ParameterProperties)
{
var propertyObject = propertyInfo.PropertyInfo.GetValue(param);
urlTarget = Regex.Replace(
urlTarget,
"{" + propertyInfo.Name + "}",
Uri.EscapeDataString(
settings.UrlParameterFormatter.Format(
propertyObject,
propertyInfo.PropertyInfo,
propertyInfo.PropertyInfo.PropertyType
) ?? string.Empty
),
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant
);
}
//don't continue here as we want it to fall through so any parameters on this object not bound here get passed as query parameters
}
else
if (!parameterMapValue.IsObjectPropertyParameter)
{
string pattern;
string replacement;
if (parameterMapValue.Type == ParameterType.RoundTripping)
{
pattern = $@"{{\*\*{parameterMapValue.Name}}}";
var paramValue = (string)param;
replacement = string.Join(
"/",
paramValue
.Split('/')
.Select(
s =>
Uri.EscapeDataString(
settings.UrlParameterFormatter.Format(
s,
restMethod.ParameterInfoMap[i],
restMethod.ParameterInfoMap[i].ParameterType
) ?? string.Empty
)
)
);
}
else
{
pattern = "{" + parameterMapValue.Name + "}";
replacement = Uri.EscapeDataString(
settings.UrlParameterFormatter.Format(
param,
restMethod.ParameterInfoMap[i],
restMethod.ParameterInfoMap[i].ParameterType
) ?? string.Empty
);
}
urlTarget = Regex.Replace(
urlTarget,
pattern,
replacement,
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant
);
isParameterMappedToRequest = true;
}
}
Expand Down Expand Up @@ -976,6 +912,7 @@ await content
restMethod.ToRestMethodInfo();
#endif
var urlTarget = BuildRelativePath(basePath, restMethod, paramList);
// NB: The URI methods in .NET are dumb. Also, we do this
// UriBuilder business so that we preserve any hardcoded query
// parameters as well as add the parameterized ones.
Expand Down Expand Up @@ -1220,6 +1157,93 @@ RestMethodInfoInternal restMethod
};
}

string BuildRelativePath(string basePath, RestMethodInfoInternal restMethod, object[] paramList)
{
basePath = basePath == "/" ? string.Empty : basePath;
var pathFragments = restMethod.FragmentPath;
if (pathFragments.Count == 0)
{
return basePath;
}
if (string.IsNullOrEmpty(basePath) && pathFragments.Count == 1)
{
return GetPathFragmentValue(restMethod, paramList, pathFragments[0]);
}

#pragma warning disable CA2000
var vsb = new ValueStringBuilder(stackalloc char[512]);
#pragma warning restore CA2000
vsb.Append(basePath);

foreach (var fragment in pathFragments)
{
vsb.Append(GetPathFragmentValue(restMethod, paramList, fragment));
}

return vsb.ToString();
}

private string GetPathFragmentValue(RestMethodInfoInternal restMethod, object[] paramList, ParameterFragment fragment)
{
if (fragment.IsConstant)
{
return fragment.Value!;
}

restMethod.ParameterMap.TryGetValue(fragment.ArgumentIndex, out var parameterMapValue);
Debug.Assert(parameterMapValue is not null);

if (fragment.IsObjectProperty)
{
var param = paramList[fragment.ArgumentIndex];
var property = parameterMapValue.ParameterProperties[fragment.PropertyIndex];

Check warning on line 1199 in Refit/RequestBuilderImplementation.cs

View workflow job for this annotation

GitHub Actions / build / build

Dereference of a possibly null reference.
var propertyObject = property.PropertyInfo.GetValue(param);

return Uri.EscapeDataString(settings.UrlParameterFormatter.Format(
propertyObject,
property.PropertyInfo,
property.PropertyInfo.PropertyType
) ?? string.Empty);
}

if (fragment.IsDynamicRoute)
{
var param = paramList[fragment.ArgumentIndex];

if (parameterMapValue.Type == ParameterType.RoundTripping)
{
var paramValue = (string)param;
return string.Join(
"/",
paramValue
.Split('/')
.Select(
s =>
Uri.EscapeDataString(
settings.UrlParameterFormatter.Format(
s,
restMethod.ParameterInfoMap[fragment.ArgumentIndex],
restMethod.ParameterInfoMap[fragment.ArgumentIndex].ParameterType
) ?? string.Empty
)
)
);
}
else
{
return Uri.EscapeDataString(
settings.UrlParameterFormatter.Format(
param,
restMethod.ParameterInfoMap[fragment.ArgumentIndex],
restMethod.ParameterInfoMap[fragment.ArgumentIndex].ParameterType
) ?? string.Empty
);
}
}

return string.Empty;

Check warning on line 1244 in Refit/RequestBuilderImplementation.cs

View check run for this annotation

Codecov / codecov/patch

Refit/RequestBuilderImplementation.cs#L1244

Added line #L1244 was not covered by tests
}

private static bool IsBodyBuffered(
RestMethodInfoInternal restMethod,
HttpRequestMessage? request
Expand Down
Loading

0 comments on commit 39e0db7

Please sign in to comment.