HCR083¶
Configure BaseAddress for typed clients that use relative URLs.
Why¶
Typed clients often call endpoints with relative URLs such as "/payments". Those calls require a configured HttpClient.BaseAddress; without one, the request fails at runtime or depends on every call site switching to absolute URLs.
Bad¶
services.AddHttpClient<PaymentsClient>();
public sealed class PaymentsClient(HttpClient client)
{
public Task<HttpResponseMessage> SendAsync(CancellationToken cancellationToken)
{
return client.GetAsync("/payments", cancellationToken);
}
}
Better¶
services.AddHttpClient<PaymentsClient>(client =>
{
client.BaseAddress = new Uri("https://api.example.com");
});
Absolute URLs are also valid when a typed client intentionally calls multiple hosts.
Current Detection¶
The implementation reports registered typed clients from AddHttpClient<TClient>() and AddHttpClient<TService, TImplementation>() when their implementation type uses a relative string or semantic System.Uri URL in common HttpClient calls such as GetAsync(...), PostAsync(...), PutAsync(...), PatchAsync(...), DeleteAsync(...), GetStringAsync(...), GetStreamAsync(...), or GetByteArrayAsync(...), and in framework System.Net.Http.Json buffered, streaming, and write extensions. String and semantic URI values may be inline, wrapped in parentheses or null-forgiving operators, or flow through visible reassignment-aware, cycle-safe locals. The analyzer also checks relative request URIs in inline or visible local HttpRequestMessage instances passed to HttpClient.Send(...) or SendAsync(...), including null-forgiving request expressions, string or System.Uri constructor arguments, RequestUri object initializers and visible property assignments, and split declarations and assignments when no later reassignment obscures the value.
Registration receivers, including aliased IServiceCollection parameters, are validated semantically when their declarations are available, and resolved AddHttpClient methods must belong to the Microsoft DI or global namespace. The analyzer skips registrations that visibly set the real HttpClient.BaseAddress property in the AddHttpClient(...) configuration delegate, a framework-owned chained ConfigureHttpClient(...) delegate, or a later ConfigureHttpClient(...) call on the same visible builder local before reassignment. Builder-local matching is symbol-aware, so a shadowed local with the same name cannot configure the outer registration. Resolved custom AddHttpClient or ConfigureHttpClient extensions and custom BaseAddress properties are not treated as framework evidence. It also skips absolute HTTP/HTTPS URLs, unregistered client classes, resolved custom HttpClient lookalikes, and custom extensions that reuse HTTP or JSON method names on a real HttpClient.
Suppression¶
Suppress only when the relative URL is rewritten by a visible framework layer the analyzer cannot inspect, or when the typed client is intentionally incomplete in sample/test code.