Skip to content

[Feature]: generate inline (reflection-free) request building for query parameters #2191

Description

@glennawatson

Follow-up from #2174 (v13.0.0).

Is your feature request related to a problem?

#2174 made path/route-template parameters generate inline (reflection-free). Query parameters still fall back to the reflection request builder:

  • auto-appended query params: [Get("/users")] Task<T> Search(string q) -> /users?q=...
  • [Query] (including [Query(Format=...)], nested/collection expansion, custom delimiters)
  • [AliasAs] on a query parameter

A method with any of these is emitted against BuildRestResultFuncForMethod (the reflection tail), so it throws at runtime under AddRefitGeneratedClient (generated-only) or NativeAOT. Query params are the most common Refit shape, so the generated-only/AOT path is still unusable for typical query APIs. The runtime symptom is #2185.

Describe the solution you'd like

Generate inline request building for methods whose only otherwise-unsupported parameters are query params. The generated query string must match the reflection path's escaping and formatting so the RequestUri is identical between the two.

Describe alternatives you've considered

Describe suggestions on how to achieve the feature

Generator (src/InterfaceStubGenerator.Shared/):

  • Parser.Request.cs: ParseRequestParameter returns UnsupportedRequestParameter (~line 357) for a parameter with no {...} placeholder. Add a RequestParameterKind.Query, classify plain / [Query] / [AliasAs] params into it, and relax canGenerateInline in ParseRequest.
  • Reuse the existing [Query] parsing (ParseQueryAttribute*, ~lines 580-625) and IsSimpleType (Parser.Request.Helpers.cs).

Runtime (src/Refit/):

  • Add a query-append helper modeled on GeneratedRequestRunner.BuildRequestPath: linear scan, ValueStringBuilder, StringHelpers.EscapeDataString per value, handling collection expansion and delimiters. The reflection version is in RequestBuilderImplementation.RequestBuilding.cs.

Guardrails:

  • Value formatting still goes through IUrlParameterFormatter; keep that boundary, the separate value-formatting issue covers it.
  • Keep the RF006 fallback analyzer and GeneratedRequestBuildingFallbackContractTests in sync: as these shapes become inline, their expectedFallback cases flip.

Tests: emit-load-invoke asserting the final RequestUri (single param, multiple, [AliasAs], [Query(Format=...)], collection, null/empty, and a value that needs escaping such as "a b/c") plus Verify snapshots.

Additional context

Deferred from the #2174 review. Related: #2185, #2168, #2169, #2182. Dotted ({request.Prop}) and round-trip ({**param}) placeholders stay on the reflection fallback (#2046) and are already covered by tests.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Featureup-for-grabs🔗 area: url-routingURL building, path/query params, segments, collection format🔴 priority: highImportant: correctness, build, or AOT breakage on common paths🧬 area: source-genStub source generator, Roslyn, diagnostics, MSBuild integration🪶 area: aot-trimmingNativeAOT, trimming, linker warnings

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions