Skip to content
9 changes: 5 additions & 4 deletions src/InterfaceStubGenerator.Shared/Emitter.Inline.Method.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ internal static InlineMethodFragments BuildInlineMethodFragments(
// the base-address merge; every other method builds a relative URI merged onto the base address.
var requestUriExpression = HasUrlParameter(request)
? $"new global::System.Uri({requestPathExpression}, global::System.UriKind.Absolute)"
: BuildRelativeUriExpression(request, requestPathExpression, settingsLocal);
: BuildRelativeUriExpression(request, requestPathExpression, settingsLocal, bodyIndent);
var (formFieldsSource, formFieldsFieldName) = BuildFormFieldsField(
plan.BodyParameter,
uniqueNames,
Expand Down Expand Up @@ -110,7 +110,7 @@ internal static void AppendInlineStandardRefitMethod(
// the base-address merge; every other method builds a relative URI merged onto the base address.
var requestUriExpression = HasUrlParameter(request)
? $"new global::System.Uri({requestPathExpression}, global::System.UriKind.Absolute)"
: BuildRelativeUriExpression(request, requestPathExpression, settingsLocal);
: BuildRelativeUriExpression(request, requestPathExpression, settingsLocal, bodyIndent);
var (formFieldsSource, formFieldsFieldName) = BuildFormFieldsField(
plan.BodyParameter,
uniqueNames,
Expand Down Expand Up @@ -149,8 +149,9 @@ internal static void AppendInlineStandardRefitMethod(
.Append(bodyIndent).Append("var ").Append(settingsLocal).Append(" = ").Append(settingsFieldName).AppendLine(";")
.Append(prologue)
.Append(bodyIndent).Append("var ").Append(requestLocal)
.Append(" = new global::System.Net.Http.HttpRequestMessage(").Append(httpMethodExpression)
.Append(", ").Append(requestUriExpression).AppendLine(");")
.Append(" = new global::System.Net.Http.HttpRequestMessage(")
.AppendLine().Append(bodyIndent).Append(httpMethodExpression)
.Append(",").AppendLine().Append(bodyIndent).Append(requestUriExpression).AppendLine(");")
.Append(contentSource)
.Append(headerSource);
AppendInlineRequestProperties(builder, request, interfaceModel, methodModel, requestLocal, settingsLocal);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,12 +207,14 @@ internal static string GetParametersArg(
replacements.Sort(static (left, right) => left.Start.CompareTo(right.Start));

var parametersSb = new PooledStringBuilder();
var indent = Indent(MethodBodyIndentation + MethodBodyIndentation);

var first = true;
foreach (var replacement in replacements)
{
if (!first)
{
_ = parametersSb.Append(", ");
_ = parametersSb.AppendLine(",").Append(indent);
}

first = false;
Expand Down
14 changes: 13 additions & 1 deletion src/InterfaceStubGenerator.Shared/Emitter.Inline.Path.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,21 @@ internal static string BuildInlinePathExpression(
{
// A template with placeholders but no bound path parameters still runs the unmatched-placeholder
// check so AllowUnmatchedRouteParameters keeps its reflection-path semantics.
var indent = Indent(MethodBodyIndentation + MethodBodyIndentation);
return TryBuildInlinePathFastExpression(request, parameterInfoNames, emission)
Comment thread
jgarciadelanoceda marked this conversation as resolved.
Outdated
?? (parameters.Length > 0 || request.Path.IndexOf('{') >= 0
? $"global::Refit.GeneratedRequestRunner.BuildRequestPath({ToCSharpStringLiteral(request.Path)}, {settingsLocal}.AllowUnmatchedRouteParameters{parameters})"
? new PooledStringBuilder()
.AppendLine("global::Refit.GeneratedRequestRunner.BuildRequestPath(")
.Append(indent)
.Append(ToCSharpStringLiteral(request.Path))
.AppendLine(",")
.Append(indent)
.Append(settingsLocal)
.AppendLine(".AllowUnmatchedRouteParameters")
.Append(indent)
.Append(parameters)
.Append(")")
.ToString()
: ToCSharpStringLiteral(request.Path));
}

Expand Down
30 changes: 26 additions & 4 deletions src/InterfaceStubGenerator.Shared/Emitter.Inline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -586,13 +586,35 @@ internal static bool HasUrlParameter(in RequestModel request) =>
/// <param name="request">The parsed request model.</param>
/// <param name="requestPathExpression">The built path-and-query expression.</param>
/// <param name="settingsLocal">The generated settings local name.</param>
/// <param name="bodyIndent">The indentation for the body.</param>
/// <returns>The generated <c>BuildRelativeUri</c> call.</returns>
/// <remarks>A <c>[QueryUriFormat]</c> method re-encodes the whole path and query with the attribute's UriFormat,
/// matching the reflection builder's final GetComponents pass; every other method uses the direct relative URI.</remarks>
internal static string BuildRelativeUriExpression(in RequestModel request, string requestPathExpression, string settingsLocal) =>
request.QueryUriFormat is { } queryUriFormat
? $"global::Refit.GeneratedRequestRunner.BuildRelativeUri(this.Client, {requestPathExpression}, {settingsLocal}.UrlResolution, (global::System.UriFormat){queryUriFormat})"
: $"global::Refit.GeneratedRequestRunner.BuildRelativeUri(this.Client, {requestPathExpression}, {settingsLocal}.UrlResolution)";
internal static string BuildRelativeUriExpression(in RequestModel request, string requestPathExpression, string settingsLocal, string bodyIndent)
{
var buffer = new PooledStringBuilder()
.AppendLine("global::Refit.GeneratedRequestRunner.BuildRelativeUri(")
.Append(bodyIndent)
.AppendLine("this.Client,")
.Append(bodyIndent)
.Append(requestPathExpression)
.AppendLine(",")
.Append(bodyIndent)
.Append(settingsLocal)
.Append(".UrlResolution");
if (request.QueryUriFormat is { } queryUriFormat)
{
_ = buffer
.AppendLine(",")
.Append(bodyIndent)
.Append("(global::System.UriFormat)")
.Append(queryUriFormat);
}

return buffer
.Append(')')
.ToString();
}

/// <summary>Finds the first request parameter of the given kind.</summary>
/// <param name="request">The request model to inspect.</param>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ public async Task SwitchOnGeneratesInlineForQueryUriFormat()
generatedRequestBuilding: true);

await Assert.That(generated).DoesNotContain(ReflectiveRequestBuilderCall);
await Assert.That(generated).Contains(".UrlResolution, (global::System.UriFormat)");
await Assert.That(generated).Contains(".UrlResolution,");
await Assert.That(generated).Contains("(global::System.UriFormat)");
}

/// <summary>Verifies custom HTTP method attributes are discovered but fall back to the runtime builder.</summary>
Expand Down Expand Up @@ -113,7 +114,7 @@ public interface IGeneratedClient

// The custom verb is allocated once in a static field and the request references it, not a per-call allocation.
await Assert.That(generated).Contains("private static readonly global::System.Net.Http.HttpMethod ______httpMethod = new global::System.Net.Http.HttpMethod(\"PURGE\");");
await Assert.That(generated).Contains("new global::System.Net.Http.HttpRequestMessage(______httpMethod,");
await Assert.That(generated).Contains("______httpMethod,");
}

/// <summary>Verifies a custom HTTP QUERY verb attribute (a draft-standard body-carrying method) with an explicit
Expand Down Expand Up @@ -218,7 +219,7 @@ public async Task SwitchOnRemovesWhitespaceAndEmptyQueryKeysFromInlineConstantPa
GeneratedClientHintName,
generatedRequestBuilding: true);

await Assert.That(generated).Contains("BuildRelativeUri(this.Client, \"/foo?one=1&two\"");
await Assert.That(generated).Contains("\"/foo?one=1&two\"");
await Assert.That(generated).DoesNotContain("drop");
await Assert.That(generated).DoesNotContain(ReflectiveRequestBuilderCall);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ public interface IGeneratedClient
var generated = result.GeneratedSources[GeneratedClientHintName];

await Assert.That(result.CompilesWithoutErrors).IsTrue();
await Assert.That(generated).Contains("""GeneratedRequestRunner.BuildRequestPath("/a?b={bVal}", refitSettings.AllowUnmatchedRouteParameters, [((5, 11), """);
await Assert.That(generated).Contains("[((5, 11), ");
}

/// <summary>Verifies that auto-appended query parameters generate inline query construction.</summary>
Expand Down
12 changes: 6 additions & 6 deletions src/tests/Refit.GeneratorTests/GeneratedRequestBuildingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ public async Task SwitchOnStripsFragmentsFromInlineConstantPaths()
GeneratedClientHintName,
generatedRequestBuilding: true);

await Assert.That(generated).Contains("BuildRelativeUri(this.Client, \"/foo?key=value\"");
await Assert.That(generated).Contains("\"/foo?key=value\",");
await Assert.That(generated).DoesNotContain("#name");
await Assert.That(generated).DoesNotContain(ReflectiveRequestBuilderCall);
}
Expand All @@ -228,7 +228,7 @@ public async Task SwitchOnStripsQueryAfterFragmentFromInlineConstantPaths()
GeneratedClientHintName,
generatedRequestBuilding: true);

await Assert.That(generated).Contains("BuildRelativeUri(this.Client, \"/foo\"");
await Assert.That(generated).Contains("\"/foo\",");
await Assert.That(generated).DoesNotContain("?key=value");
await Assert.That(generated).DoesNotContain(ReflectiveRequestBuilderCall);
}
Expand All @@ -246,7 +246,7 @@ public async Task SwitchOnRemovesEmptyQueryKeysFromInlineConstantPaths()
GeneratedClientHintName,
generatedRequestBuilding: true);

await Assert.That(generated).Contains("BuildRelativeUri(this.Client, \"/foo?key=&two=2\"");
await Assert.That(generated).Contains("\"/foo?key=&two=2\",");
await Assert.That(generated).DoesNotContain("=drop");
await Assert.That(generated).DoesNotContain(ReflectiveRequestBuilderCall);
}
Expand Down Expand Up @@ -363,9 +363,9 @@ public interface IGeneratedClient
GeneratedClientHintName,
generatedRequestBuilding: true);

await Assert.That(generated).Contains("BuildRelativeUri(this.Client, \"/global\"");
await Assert.That(generated).Contains("BuildRelativeUri(this.Client, \"/alias\"");
await Assert.That(generated).Contains("BuildRelativeUri(this.Client, \"/qualified\"");
await Assert.That(generated).Contains("\"/global\",");
await Assert.That(generated).Contains("\"/alias\",");
await Assert.That(generated).Contains("\"/qualified\",");
await Assert.That(generated).Contains("HttpMethod.Get");
await Assert.That(generated).Contains("HttpMethod.Post");
await Assert.That(generated).Contains("HttpMethod.Put");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public async Task GeneratedInlinesNoLeadingSlashPath()
{
var generated = string.Concat(Fixture.RunGenerator(ApiSource, generatedRequestBuilding: true).GeneratedSources.Values);

await Assert.That(generated).Contains("BuildRequestPath(\"relative/{id}\"");
await Assert.That(generated).Contains("\"relative/{id}\",");
await Assert.That(generated).DoesNotContain("BuildRestResultFuncForMethod(\"Relative\"");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,12 @@ public RefitGeneratorTestIGeneratedClient(global::System.Net.Http.HttpClient cli
public global::System.Threading.Tasks.Task<string> Get()
{
var refitSettings = _settings;
var refitRequest = new global::System.Net.Http.HttpRequestMessage(global::System.Net.Http.HttpMethod.Get, global::Refit.GeneratedRequestRunner.BuildRelativeUri(this.Client, "/users", refitSettings.UrlResolution));
var refitRequest = new global::System.Net.Http.HttpRequestMessage(
global::System.Net.Http.HttpMethod.Get,
global::Refit.GeneratedRequestRunner.BuildRelativeUri(
this.Client,
"/users",
refitSettings.UrlResolution));
global::Refit.GeneratedRequestRunner.AddConfiguredRequestOptions(refitRequest, refitSettings, typeof(global::RefitGeneratorTest.IGeneratedClient));
global::Refit.GeneratedRequestRunner.AddRequestProperty<string>(refitRequest, global::Refit.HttpRequestMessageOptions.MethodName, "Get");
global::Refit.GeneratedRequestRunner.AddRequestProperty<string>(refitRequest, global::Refit.HttpRequestMessageOptions.RelativePathTemplate, "/users");
Expand Down
Loading
Loading