github reactiveui/refit v16.4.0
16.4.0

2 hours ago

Summary

Refit 16.4.0 adds opt-in HTTP-aware retries, new query naming and relative URL options, and fixes for parameter binding and response handling. Existing clients keep their current behavior unless you enable the new options.

HTTP-aware retries

HttpRetryHandler works with Refit clients and ordinary HttpClient calls. It needs no extra resilience package or changes to your API interface.

  • By default, it allows three retries after the first send for GET, HEAD, OPTIONS and TRACE. It retries HTTP 408, 429, 500, 502, 503 and 504, plus HttpRequestException. A 403 retries only when the configured delay reader finds a valid timing hint.
  • The default reader respects standard Retry-After headers. RetryDelayProviders.Azure, .Aws, .GitHub and .Jira also read provider-specific timing headers. Custom readers can use ReadHeader with seconds, milliseconds, Unix timestamps or date formats.
  • When no valid hint is available, retries use exponential backoff starting at one second and capped at 30 seconds. Full jitter randomly spreads attempts within that interval. You can disable jitter or choose symmetric variation with RetryJitterMode.Symmetric.
  • Server hints are never shortened by jitter. A hint above MaxDelay returns the response rather than retrying too early. ServerDelayJitterRatio can add a small positive variation to a server delay.
  • Each attempt uses a fresh request with copied headers, options and content metadata. Request bodies are buffered once, with a one MiB default limit. Failed responses are disposed; the final response remains caller-owned. Cancellation covers buffering, sends and waits.

Quick start

Put the retry handler in the client's handler chain, then create your Refit client as usual:

using Refit;

var handler = new HttpRetryHandler(new HttpRetryOptions
{
    MaxRetries = 3,
    DelayProvider = RetryDelayProviders.GitHub,
})
{
    InnerHandler = new HttpClientHandler(),
};

using var client = new HttpClient(handler)
{
    BaseAddress = new Uri("https://api.github.com"),
};
var api = RestService.ForGenerated<IGitHubApi>(client);

IGitHubApi is your existing Refit interface. Omit DelayProvider to use standard Retry-After handling. With Refit.HttpClientFactory, register a fresh handler for the client:

services.AddRefitGeneratedClient<IGitHubApi>()
    .ConfigureHttpClient(static client =>
        client.BaseAddress = new Uri("https://api.github.com"))
    .AddHttpMessageHandler(static () => new HttpRetryHandler(new HttpRetryOptions
    {
        DelayProvider = RetryDelayProviders.GitHub,
    }));

Set MaxRetries = 0 to disable retries. Use ShouldRetry to replace the response rules, and BaseDelay / MaxDelay to tune waits. POST, PUT, PATCH and DELETE require RetryUnsafeMethods = true; enable this only when replay is safe for the endpoint, such as when it supports an idempotency key. Avoid unbounded streaming uploads and stacking this handler with another retry strategy.

An authentication handler inside the retry handler runs on every attempt. HttpClient.Timeout bounds the whole call, including retry waits. For deterministic tests, Refit.Testing adds RetryReply.After, RetryReply.Header and StubHttp.CreateRetryHandler, with TimeProvider support.

Other changes

  • [Query(Name = "client_id")] names a query parameter or form field. [AliasAs] still takes precedence. The positional [Query("...")] argument remains a nesting delimiter, not an alias; the new opt-in diagnostic RF016 helps catch that mistake.
  • [Url(AllowRelative = true)] accepts relative as well as absolute URLs, using normal URI resolution against HttpClient.BaseAddress. Existing encoded queries are retained when appending query parameters. Plain [Url] still requires an absolute URL.
  • RefitSettings now groups options under Serialization, Url, Exceptions and Requests. Existing top-level properties remain available and refer to the same settings. Retry options similarly expose Backoff, Replay and Responses groups.
  • The generator pipeline and model equality handling were reorganized, with added coverage for inherited interfaces, incremental generation and request construction.
  • New tested documentation recipes cover resilience and OpenTelemetry. CI now publishes and runs a consumer of the packed Refit packages under Native AOT on Windows, Linux and macOS, as well as the documentation NativeClient.
  • Dependency and build updates include ReactiveUI.Primitives 9 and refreshed test packages and GitHub Actions. Local Refit packages are excluded from Renovate updates.

Fixes

  • Route and query parameters whose names differ only by case now bind correctly in generated and reflection-based clients. Exact-case route matches take priority, and sibling query parameters no longer overwrite route values. This also covers mixed ordinary and catch-all placeholders. (#2360)
  • IApiResponse.ContentHeaders.ContentLength remains readable after response content is buffered or disposed, including when no explicit Content-Length header was sent. (#2360)
  • If response buffering fails mid-stream and deserialization then fails because the stream was consumed, Refit surfaces the original buffering exception instead of the misleading deserialization error. Content that remains readable can still be deserialized. (#2360)
  • Native AOT smoke publishing on .NET 11 RC2 no longer fails while trying to copy a missing XML documentation file. (#2360)

๐Ÿ—ž๏ธ What's Changed

โœจ Features

๐Ÿ› Fixes

  • 8e03c5e fix: disambiguate case-differing parameters, preserve ContentLength header, and surface buffering exceptions (#2360) @glennawatson

๐Ÿงน General Changes

๐Ÿ“ฆ Dependencies

  • 7c2efd1 chore(deps): update dependency tunit to 1.69.16 (#2341) renovate[bot]
  • db1db92 chore(deps): update dependency reactiveui.primitives to v9 (#2350) renovate[bot]
  • fbf8edd chore(deps): update dependency tunit to 1.72.10 (#2346) renovate[bot]
  • e692f9b chore(deps): update dependency tunit to 1.69.21 (#2343) renovate[bot]
  • 8d2e79b chore(deps): update github actions (#2353) renovate[bot]

๐Ÿ”— Full Changelog: v16.3.0...v16.4.0

๐Ÿ™Œ Contributions

๐Ÿ’– Thanks to all the contributors: @glennawatson

๐Ÿค– Automated services that contributed: renovate[bot]

Don't miss a new refit release

NewReleases is sending notifications on new releases.