Migration Guides
v10.2.0 → v10.2.1
v10.2.1 removes the ProxySettings class. v10.2.0 had already removed the built-in proxy pipeline (ProxyMessageHandler, WithProxyFromSettings<TSettings>(...)); the ProxySettings POCO itself is now gone too.
What changed
| Aspect | v10.2.0 | v10.2.1 |
|---|---|---|
ProxySettings | configuration-only POCO (not consumed by the package) | removed — define your own options type |
UseProxy flag on IIntegrationSettings/BaseApiSettings<T> | unconsumed | still present, still unconsumed |
Migration Steps
Replace the ProxySettings property on your settings class with your own options POCO and wire the primary handler explicitly.
Before (v10.2.0):
public class MyApiSettings : BaseApiSettings<MyApiActions>
{
public ProxySettings? Proxy { get; set; }
}
After (v10.2.1):
public sealed class ProxyOptions
{
[Required, Url] public string Address { get; set; } = default!;
public string? Username { get; set; }
public string? Password { get; set; }
public bool BypassProxyOnLocal { get; set; }
}
public class MyApiSettings : BaseApiSettings<MyApiActions>
{
public ProxyOptions? Proxy { get; set; }
}
builder.Services.AddHttpClient("MyApi")
.ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler
{
Proxy = new WebProxy(proxyOptions.Address),
UseProxy = true
});
v10.1.1 → v10.2.0
v10.2.0 consolidates the registration API around C# 14 extension members — one extension class per receiver type — and removes the built-in API key and proxy machinery. Extension-method call sites (services.AddHttpClientIntegration(...), builder.AddLoggingHandler(), ...) are unchanged; only class-name references and the removed members need attention.
Why this change?
DependencyInjectionExtensionsmixedIServiceCollectionmethods andIHttpClientBuildermethods in one class; the split (ServiceCollectionExtensions+HttpClientBuilderExtensions) follows the ASP.NET Core naming convention and matches how C# 14 extension blocks group members by receiver type.- The API key and proxy handlers (
ApiKeyAuthenticationHandler<TSettings>,ProxyMessageHandler, and theirWith*registration methods) saw little use and are straightforward to replace with a customDelegatingHandlerwhen needed.
What changed
| Aspect | v10.1.1 | v10.2.0 |
|---|---|---|
| Registration class | DependencyInjectionExtensions (all methods) | ServiceCollectionExtensions (Add* methods) + HttpClientBuilderExtensions (pipeline methods) |
| Method style | classic static extension methods | C# 14 extension members — call sites unchanged |
| Attach logging handler | WithHttpLoggingHandler() | AddLoggingHandler() |
| Attach header forwarding | WithInternalHeadersForwardingHandler() | AddInternalHeadersForwardingHandler() |
| Logging handler type | RequestResponseLoggingHandler | LoggingHandler (same behavior) |
| Request options utilities | HttpRequestMessageExtensions | HttpRequestOptionsExtensions (GetClientIpAddress() only) |
| API key support | IApiKeySettings, ApiKeyAuthenticationHandler<TSettings>, WithApiKey(...), WithApiKeyFromSettingsHandler<TSettings>() | removed — use a custom DelegatingHandler or ConfigureHttpClient |
| Proxy support | IProxySettings, ProxyMessageHandler, WithProxyFromSettings<TSettings>(...), ProxySettings : IProxySettings | removed — ProxySettings remains a configuration-only POCO; use ConfigurePrimaryHttpMessageHandler for a proxy |
InternalHeadersForwardHandler | captured headers once in the constructor | reads Authorization / x-request-id per request (no code change needed) |
Migration Steps
1. Update class-name references
Only code that references a class by name — a typeof(...) expression, a static call, or a reflection string — needs updating. Receiver-style calls such as services.AddHttpClientIntegration(...) keep working unchanged.
// ❌ v10.1.1
var t = typeof(DependencyInjectionExtensions);
// ✅ v10.2.0 — pick the class that holds the members you reference
var t = typeof(ServiceCollectionExtensions); // AddHttpClientIntegration, Add*HttpMessageHandler
var b = typeof(RA.Utilities.Integrations.Extensions.HttpClientBuilderExtensions); // AddLoggingHandler, AddInternalHeadersForwardingHandler
HttpClientBuilderExtensions shares its name with Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions (the built-in AddHttpMessageHandler<T>/ConfigureHttpClient class). When both namespaces are imported, reference it fully qualified in typeof(...) or static calls. Receiver-style calls such as builder.AddLoggingHandler() are unaffected.
2. Rename the pipeline attachment methods
// ❌ v10.1.1
services.AddHttpClientIntegration<IMyApiClient, MyApiClient, MyApiSettings>(section)
.WithHttpLoggingHandler()
.WithInternalHeadersForwardingHandler();
// ✅ v10.2.0
services.AddHttpClientIntegration<IMyApiClient, MyApiClient, MyApiSettings>(section)
.AddLoggingHandler()
.AddInternalHeadersForwardingHandler();
3. Rename the logging handler type
// ❌ v10.1.1
services.AddScopedHttpMessageHandler<RequestResponseLoggingHandler>();
// ✅ v10.2.0
services.AddScopedHttpMessageHandler<LoggingHandler>();
Manual constructions update the generic type argument: new LoggingHandler(logger) where logger is an ILogger<LoggingHandler>.
4. Replace API key and proxy usage
The API key and proxy members were removed without built-in replacements:
// ❌ v10.1.1 — removed
builder.WithApiKey("my-secret-api-key");
builder.WithApiKeyFromSettingsHandler<MyApiSettings>();
builder.WithProxyFromSettings(settings => settings.Proxy);
// ✅ v10.2.0 — static API key via default request headers
builder.ConfigureHttpClient(client => client.DefaultRequestHeaders.Add("X-Api-Key", "my-secret-api-key"));
// ✅ v10.2.0 — proxy via the primary handler
builder.ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler
{
Proxy = new WebProxy("http://proxy.server:8080", BypassOnLocal: true)
{
Credentials = new NetworkCredential("proxy-user", "proxy-password")
}
});
For dynamic, per-request API keys, write a small DelegatingHandler that adds the header in SendAsync and attach it with AddHttpMessageHandler<T>() / AddTransientHttpMessageHandler<T>(). Settings classes no longer implement IApiKeySettings/IProxySettings — the ApiKey/Proxy properties themselves remain plain, bindable options properties.
5. Optional: adopt the request options utility rename
request.Options.GetClientIpAddress() works identically in both versions — only class-name references change (HttpRequestMessageExtensions → HttpRequestOptionsExtensions). The ToDictionary(...) helper was removed; inline headers?.ToDictionary(h => h.Key, h => string.Join(",", h.Value)) where needed.
v10.0.1 → v10.1.0
v10.1.0 reworks the BaseHttpClient request surface: headers are now strongly typed, all four HTTP verbs are supported, JSON is the fixed content type, and query/header parameter mappings can be generated at compile time. Most call sites need small mechanical updates; none of the changes affect how the HTTP requests themselves are sent.
Why this change?
- Header dictionaries (
Dictionary<string, string>) offered no compile-time safety and allowed typos in header names. - The settings model carried
MediaType/Encoding, but JSON was the only supported format anyway. - Query parameter models had to hand-write the
IQueryStringRequestcontract; a source generator now does that work.
What changed
| Aspect | v10.0.1 | v10.1.0 |
|---|---|---|
| HTTP verbs | GetAsync, PostAsync, PutAsync | + DeleteAsync |
| Headers parameter | Dictionary<string, string>? | IHeaderRequest? |
| Header classes | n/a | [HeaderParameters] + generated IHeaderRequest |
| Query classes | hand-written IQueryStringRequest | [QueryParameters] + generated IQueryStringRequest (hand-written still works) |
| Settings media type | IIntegrationSettings.MediaType / Encoding | removed — JSON only |
| Typed responses | Task<TResponse> | Task<TResponse?> (null for empty bodies) |
AddOptionWithValidations(...) | available | removed |
| Query string extension | DictionaryExtensions.ToQueryString(this QueryParams) | removed — use QueryUtilities.ToQueryString(QueryParams?) |
Migration Steps
1. Replace header dictionaries with header classes
Before (v10.0.1):
var headers = new Dictionary<string, string>
{
["x-request-id"] = "trace-1"
};
var product = await client.GetAsync<Product>("products/1", headers: headers);
After (v10.1.0):
using RA.Utilities.Integrations.Attributes;
[HeaderParameters]
public partial class RequestHeaders
{
[HeaderParameterName("x-request-id")]
public string? XCorrelationId { get; init; }
}
var product = await client.GetAsync<Product>(
"products/1",
headers: new RequestHeaders { XCorrelationId = "trace-1" });
2. Mark query parameter classes with [QueryParameters]
Hand-written IQueryStringRequest implementations still work, but marked classes get the mapping generated:
Before (v10.0.1):
public class GetProductsQuery : IQueryStringRequest
{
public int? CategoryId { get; set; }
public QueryParams QueryStringValues()
{
var query = new QueryParams();
if (CategoryId is not null)
{
query.Add(nameof(CategoryId), CategoryId.Value.ToString());
}
return query;
}
}
After (v10.1.0):
[QueryParameters]
public partial class GetProductsQuery
{
public int? CategoryId { get; set; }
}
The class must be partial (and so must all of its containing types); the generator implements IQueryStringRequest on a generated partial part. Use [QueryParameterName("...")] to override a key.
3. Handle nullable typed responses
Before (v10.0.1):
Product product = await client.GetAsync<Product>("products/1");
After (v10.1.0):
Product? product = await client.GetAsync<Product>("products/1");
if (product is null)
{
// The server returned a successful response with an empty body.
}
4. Remove media type / encoding from settings
Before (v10.0.1):
public class MyApiSettings : BaseApiSettings<MyApiActions>
{
public override string MediaType { get; set; } = "application/json";
}
After (v10.1.0):
Delete the properties — request bodies are always sent as application/json; charset=utf-8 and responses are always parsed as JSON.
5. Replace AddOptionWithValidations usage
Before (v10.0.1):
services.AddOptionWithValidations<MyFeatureSettings>();
After (v10.1.0):
Register validated integration settings through AddHttpClientIntegration, or use the built-in options API directly:
services.AddOptions<MyFeatureSettings>()
.BindConfiguration("MyFeature")
.ValidateDataAnnotations()
.ValidateOnStart();
6. Replace DictionaryExtensions.ToQueryString(...)
Before (v10.0.1):
using RA.Utilities.Integrations.Extensions;
using RA.Utilities.Integrations.Models;
QueryParams parameters = [new("page", "1")];
string queryString = parameters.ToQueryString(); // "?page=1"
After (v10.1.0):
using RA.Utilities.Integrations.Models;
using RA.Utilities.Integrations.Utilities;
QueryParams parameters = [new("page", "1")];
string queryString = QueryUtilities.ToQueryString(parameters); // "?page=1"
Full Changelog
See the package release notes for a complete version history.