Dosaic.Extensions.RestEase
1.2.34
dotnet add package Dosaic.Extensions.RestEase --version 1.2.34
NuGet\Install-Package Dosaic.Extensions.RestEase -Version 1.2.34
<PackageReference Include="Dosaic.Extensions.RestEase" Version="1.2.34" />
<PackageVersion Include="Dosaic.Extensions.RestEase" Version="1.2.34" />
<PackageReference Include="Dosaic.Extensions.RestEase" />
paket add Dosaic.Extensions.RestEase --version 1.2.34
#r "nuget: Dosaic.Extensions.RestEase, 1.2.34"
#:package Dosaic.Extensions.RestEase@1.2.34
#addin nuget:?package=Dosaic.Extensions.RestEase&version=1.2.34
#tool nuget:?package=Dosaic.Extensions.RestEase&version=1.2.34
Dosaic.Extensions.RestEase
Dosaic.Extensions.RestEase builds typed HTTP API clients on top of RestEase. It plugs into IHttpClientFactory, uses System.Text.Json, supports a composable DelegatingHandler middleware chain, Polly v8 resilience pipelines (via Microsoft.Extensions.Http.Resilience), response caching via IDistributedCache (in-memory or Redis), and ships an OAuth2 integration with thread-safe token caching, automatic refresh-token rotation, and 401-triggered force-refresh retry.
Installation
dotnet add package Dosaic.Extensions.RestEase
<PackageReference Include="Dosaic.Extensions.RestEase" Version="" />
Features
- Typed HTTP clients — define an interface with RestEase attributes, get a fully wired client back.
- IHttpClientFactory integration — proper socket pooling, DNS refresh, named-client lifetime management.
- DelegatingHandler middleware chain — plug in correlation IDs, logging, custom auth, rate-limit headers, request signing, etc.
- Polly v8 resilience pipelines — retry with jitter + exponential backoff,
Retry-Afterhonouring, timeouts, circuit breaker, hedging, bulkhead. Powered byMicrosoft.Extensions.Http.Resilience. - Response caching — Polly v8 strategy backed by
IDistributedCache. In-memory by default, swap to Redis / SQL Server / NCache with one line. HonoursCache-Control(max-age,no-store,no-cache,private). - Client-side rate limiting — Polly v8 strategies (SlidingWindow / FixedWindow / TokenBucket / Concurrency) backed by
System.Threading.RateLimiting. ThrowsRateLimiterRejectedExceptionon rejection. - Single Polly pipeline — caching + retry + rate limit + timeout all composed into one
ResiliencePipeline<HttpResponseMessage>mounted viaAddResilienceHandler. Auto-wired fromCaching/Resilience/RateLimitsconfig blocks. Custom strategies viaAddPolly(Action<...>). - OAuth2 out of the box —
ClientCredentialsandPasswordgrants; transparent refresh-token rotation; concurrent-call coalescing viaSemaphoreSlim; 401-triggered forced refresh + retry. - Pluggable
ITokenProvider— swap in a distributed token cache, mTLS, API key, or any custom auth strategy. - System.Text.Json only — override
JsonSerializerOptionsper-client; sane web defaults out of the box. - DI builder fluent API —
AddRestEaseApi<TApi>()returnsIRestEaseClientBuilderfor composition. - Static factory for non-DI usage —
RestClientFactory.Create<TApi>(...)still available.
Quick Start (DI — recommended)
using Dosaic.Extensions.RestEase.DependencyInjection;
services.AddRestEaseApi<IUserApi>(o => o.BaseAddress = "https://api.example.com")
.AddResilience();
Resolve the typed client:
public class UserService(IUserApi api)
{
public Task<User> Get(Guid id, CancellationToken ct) => api.GetUserAsync(id, ct);
}
Without the Options pattern
Pass a RestEaseClientOptions instance directly — no lambda, no IConfiguration binding:
var options = new RestEaseClientOptions
{
BaseAddress = "https://api.example.com",
Timeout = TimeSpan.FromSeconds(30),
UserAgent = "my-service/1.0"
};
options.DefaultHeaders["X-Tenant"] = tenantId;
services.AddRestEaseApi<IUserApi>(options);
Overload signatures:
AddRestEaseApi<TApi>(this IServiceCollection, RestEaseClientOptions);
AddRestEaseApi<TApi>(this IServiceCollection, string name, RestEaseClientOptions);
The instance is copied into the named-options store at registration time. IOptionsMonitor<RestEaseClientOptions> is still wired internally (handlers depend on it) — caller does not touch it.
Interface Definition
using RestEase;
public interface IUserApi
{
[Get("users/{id}")]
Task<User> GetUserAsync([Path] Guid id, CancellationToken ct);
[Post("users")]
Task<User> CreateAsync([Body] User user, CancellationToken ct);
[Put("users/{id}")]
Task UpdateAsync([Path] Guid id, [Body] User user, CancellationToken ct);
[Delete("users/{id}")]
Task DeleteAsync([Path] Guid id, CancellationToken ct);
}
Configuration Binding
MyApi:
BaseAddress: https://api.example.com
Timeout: 00:00:30
UserAgent: my-service/1.0
Authentication:
Enabled: true
BaseUrl: https://auth.example.com
TokenUrlPath: /realms/my-realm/protocol/openid-connect/token
GrantType: ClientCredentials
ClientId: my-client
ClientSecret: s3cr3t
Scope: api.read api.write
RefreshSkew: 00:00:30
Caching:
Enabled: true
DefaultTtl: 00:05:00
MaxTtl: 00:30:00
RespectCacheControl: true
IncludeAuthorizationInKey: false
KeyPrefix: "myapi:"
Methods: [GET, HEAD]
CacheableStatusCodes: [200, 404]
Resilience:
Enabled: true
MaxRetryAttempts: 3
BaseDelay: 00:00:00.200
AttemptTimeout: 00:00:10
TotalRequestTimeout: 00:00:30
RateLimits:
Enabled: true
ThrowOnRejection: false # false = return 429 + Retry-After, true = throw HttpRequestException
SlidingWindow:
Enabled: true
PermitLimit: 50 # max requests per window
Window: 00:00:10 # window size
SegmentsPerWindow: 4 # window slices for smoothing
AutoReplenishment: true
QueueProcessingOrder: OldestFirst # OldestFirst | NewestFirst
QueueLimit: 1024
FixedWindow:
Enabled: false
PermitLimit: 100
Window: 00:00:01
AutoReplenishment: true
QueueLimit: 0
TokenBucket:
Enabled: false
PermitLimit: 100 # token bucket capacity
TokensPerPeriod: 10
ReplenishmentPeriod: 00:00:01
AutoReplenishment: true
QueueLimit: 0
Concurrency:
Enabled: true
PermitLimit: 10 # max in-flight requests
QueueLimit: 1024
Multiple limiters enabled simultaneously stack as Polly strategies in single pipeline. Order: Cache → SlidingWindow → FixedWindow → TokenBucket → Concurrency → TotalTimeout → Retry → AttemptTimeout (outermost-first).
services.AddRestEaseApiFromConfiguration<IUserApi>(configuration, "MyApi");
// single Polly pipeline auto-wired with cache + limiters + retry + timeouts
Extra Polly strategies via .AddPolly((pb, ctx) => pb.AddHedging(...)) — stacks alongside auto-wired pipeline.
Overload signatures:
AddRestEaseApiFromConfiguration<TApi>(IServiceCollection, IConfiguration, string sectionKey);
AddRestEaseApiFromConfiguration<TApi>(IServiceCollection, string name, IConfiguration, string sectionKey);
AddRestEaseApiFromConfiguration<TApi>(IServiceCollection, IConfigurationSection);
AddRestEaseApiFromConfiguration<TApi>(IServiceCollection, string name, IConfigurationSection);
DI Builder API
AddRestEaseApi<TApi>() returns an IRestEaseClientBuilder:
| Method | Purpose |
|---|---|
.ConfigureOptions(Action<RestEaseClientOptions>) |
Mutate client options |
.ConfigureJson(Action<JsonSerializerOptions>) |
Tweak the System.Text.Json options |
.ConfigureHttpClient(Action<HttpClient>) |
Raw HttpClient configuration |
.AddOAuth2(Action<AuthenticationConfig>) |
Enable the built-in OAuth2 token provider |
.AddTokenProvider<T>() |
Plug in a custom ITokenProvider (registered in DI) |
.AddHandler<THandler>() |
Insert a DelegatingHandler into the chain (outside Polly pipeline) |
.AddResilience(Action<ResilienceConfig>?) |
Enable + tune retry / timeouts |
.AddCaching(Action<HttpCacheOptions>?) |
Enable + tune response cache (IDistributedCache-backed Polly strategy) |
.AddRateLimits(Action<RateLimitsConfig>?) |
Enable + tune rate limiters (SlidingWindow / FixedWindow / TokenBucket / Concurrency) |
.AddPolly(Action<ResiliencePipelineBuilder<HttpResponseMessage>, ResilienceHandlerContext>) |
Mount additional Polly v8 pipeline (separate slot, stacks with auto-pipeline) |
OAuth2
Client Credentials
services.AddRestEaseApi<IUserApi>(o => o.BaseAddress = "https://api.example.com")
.AddOAuth2(a =>
{
a.BaseUrl = "https://auth.example.com";
a.TokenUrlPath = "/oauth/token";
a.GrantType = GrantType.ClientCredentials;
a.ClientId = "my-client";
a.ClientSecret = "s3cr3t";
a.Scope = "api.read";
})
.AddResilience();
Resource Owner Password
.AddOAuth2(a =>
{
a.BaseUrl = "https://auth.example.com";
a.TokenUrlPath = "/oauth/token";
a.GrantType = GrantType.Password;
a.ClientId = "my-client";
a.Username = "alice";
a.Password = "s3cr3t";
});
Authorization Code grant is not supported. The browser leg (user redirect + consent) is out of scope for a server-side HTTP client. Use ASP.NET OIDC (
AddOpenIdConnect) to handle the interactive login, then plug a customITokenProviderthat readsHttpContext.GetTokenAsync("access_token")and feeds it into this client.
How the OAuth2 pipeline behaves
- Concurrent-call coalescing. Multiple requests during a token refresh hit the IdP once — gated by
SemaphoreSlimwith double-checked locking. - Refresh-token rotation. When the access token expires but the refresh token is still valid, a
refresh_tokengrant is used automatically. - Clock-skew buffer.
AuthenticationConfig.RefreshSkew(default 30s) refreshes the token slightly before its real expiry. - 401 → force-refresh + retry. If the server returns 401 with our auto-injected token, the cached token is invalidated, a fresh one is fetched, and the request is retried once.
- User-supplied
Authorizationheader is respected — the handler never overwrites a header the caller already set.
Custom ITokenProvider
For distributed token caches, mTLS, API key, or anything else:
public sealed class RedisTokenProvider(IConnectionMultiplexer redis) : ITokenProvider
{
public async Task<AccessToken> GetTokenAsync(bool forceRefresh, CancellationToken ct) { /* ... */ }
public void Invalidate() { /* ... */ }
}
services.AddSingleton<RedisTokenProvider>();
services.AddRestEaseApi<IUserApi>(o => o.BaseAddress = "...")
.AddTokenProvider<RedisTokenProvider>();
Resilience
Built on Polly v8 + Microsoft.Extensions.Http.Resilience. Three equivalent ways to enable:
Builder method (most concise):
services.AddRestEaseApi<IUserApi>(o => o.BaseAddress = "https://api.example.com")
.AddResilience(r =>
{
r.MaxRetryAttempts = 3;
r.BaseDelay = TimeSpan.FromMilliseconds(200);
r.AttemptTimeout = TimeSpan.FromSeconds(10);
r.TotalRequestTimeout = TimeSpan.FromSeconds(30);
});
Inline options:
services.AddRestEaseApi<IUserApi>(o =>
{
o.BaseAddress = "https://api.example.com";
o.Resilience = new ResilienceConfig { Enabled = true, MaxRetryAttempts = 3 };
});
Config-driven: see Configuration Binding section.
Defaults: exponential backoff + jitter, HttpRetryStrategyOptions predicate (5xx + 408 + 429 + HttpRequestException, honours Retry-After).
ResilienceConfig
| Property | Type | Description |
|---|---|---|
Enabled |
bool |
Master switch (AddResilience() sets to true) |
MaxRetryAttempts |
int? |
Defaults to 3 |
BaseDelay |
TimeSpan? |
Defaults to 500ms |
AttemptTimeout |
TimeSpan? |
Per-try timeout |
TotalRequestTimeout |
TimeSpan? |
Outermost timeout across retries |
AdditionalRetryStatusCodes |
HashSet<HttpStatusCode> |
Extra retryable codes appended to default predicate |
ConfigureRetry |
Action<HttpRetryStrategyOptions>? |
Last-mile mutation of retry strategy (backoff, jitter, custom ShouldHandle, OnRetry hooks) |
Advanced retry tuning
.AddResilience(r =>
{
r.MaxRetryAttempts = 5;
r.AdditionalRetryStatusCodes.Add(HttpStatusCode.BadGateway);
r.ConfigureRetry = retry =>
{
retry.BackoffType = DelayBackoffType.Exponential;
retry.UseJitter = true;
retry.MaxDelay = TimeSpan.FromSeconds(30);
retry.OnRetry = args => { /* log */ return default; };
};
});
Custom Polly pipeline (hedging / circuit breaker / etc.)
services.AddRestEaseApi<IUserApi>(o => o.BaseAddress = "...")
.AddPolly((pb, ctx) =>
{
pb.AddHedging(new HedgingStrategyOptions<HttpResponseMessage> { MaxHedgedAttempts = 2 });
pb.AddCircuitBreaker(new CircuitBreakerStrategyOptions<HttpResponseMessage> { FailureRatio = 0.1 });
});
AddPolly mounts an additional resilience handler (separate slot — multiple calls produce stacked pipelines). Auto-pipeline (from AddResilience / AddCaching / AddRateLimits) always runs alongside.
Static factory (non-DI)
RestClientFactory.Create<T> uses Polly directly via ResiliencePipeline<HttpResponseMessage> — defaults from RestEaseDefaults.CreateDefaultPipeline() (3 retries exp + jitter, 100s overall timeout).
Middleware (DelegatingHandler chain)
public sealed class CorrelationIdHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken ct)
{
request.Headers.TryAddWithoutValidation("X-Correlation-Id", Activity.Current?.TraceId.ToString() ?? Guid.NewGuid().ToString("N"));
return base.SendAsync(request, ct);
}
}
services.AddRestEaseApi<IUserApi>(o => o.BaseAddress = "...")
.AddHandler<CorrelationIdHandler>()
.AddOAuth2(a => { /* ... */ })
.AddResilience();
Custom DelegatingHandler chain wraps the Polly resilience handler at the outside. Order:
[CorrelationId] → [OAuth2] → [Polly pipeline: cache → limiters → retry → timeout] → SocketsHttpHandler
Custom handlers run on every retry (since they're outside the Polly pipeline). Put cache inside the Polly pipeline (via Caching options) if you want hits to short-circuit auth + retries.
Response Caching
Cache implemented as Polly v8 strategy (HttpCacheResilienceStrategy) backed by IDistributedCache. Same abstraction covers in-process and out-of-process stores — swap backing implementation, strategy code unchanged.
In-memory (default)
services.AddRestEaseApi<IUserApi>(o => o.BaseAddress = "https://api.example.com")
.AddCaching(c => c.DefaultTtl = TimeSpan.FromMinutes(10));
If no IDistributedCache is already registered, AddDistributedMemoryCache() is auto-registered (per-process, no shared state).
Redis (shared across replicas)
services.AddStackExchangeRedisCache(o =>
{
o.Configuration = "redis:6379";
o.InstanceName = "my-service:";
});
services.AddRestEaseApi<IUserApi>(o => o.BaseAddress = "https://api.example.com")
.AddCaching(c => c.DefaultTtl = TimeSpan.FromMinutes(10));
Any IDistributedCache implementation works: Microsoft.Extensions.Caching.StackExchangeRedis, Microsoft.Extensions.Caching.SqlServer, NCache, etc. Register before AddRestEaseApi<T> so the cache strategy resolves your impl instead of the in-memory fallback.
HttpCacheOptions
| Property | Type | Default | Description |
|---|---|---|---|
Enabled |
bool |
true |
Master switch |
DefaultTtl |
TimeSpan |
5 min |
TTL when no Cache-Control: max-age on response |
MaxTtl |
TimeSpan? |
null |
Upper bound — clamps server-provided max-age |
Methods |
HashSet<string> |
{ "GET" } |
HTTP method names to cache (case-insensitive) |
CacheableStatusCodes |
HashSet<int> |
200, 203, 300, 301, 404, 410 |
Statuses eligible for storage |
RespectCacheControl |
bool |
true |
Honour no-store, no-cache, private, max-age |
IncludeAuthorizationInKey |
bool |
false |
When true, SHA256-hashed Authorization header gets folded into key |
KeyPrefix |
string |
dosaic:restease: |
Prefix prepended to every key |
KeyBuilder |
Func<HttpRequestMessage,string>? |
null |
Override default method + url + auth-hash key. Code-only — not bindable from config |
ShouldCacheRequest |
Func<HttpRequestMessage,bool>? |
null |
Per-request opt-out. Code-only |
ShouldCacheResponse |
Func<HttpResponseMessage,bool>? |
null |
Per-response opt-out. Code-only |
Config-driven caching
HttpCacheOptions lives on RestEaseClientOptions.Caching — bind from same config section:
Api:
BaseAddress: https://api.example.com
Caching:
Enabled: true
DefaultTtl: 00:10:00
KeyPrefix: "myapi:"
Methods: [GET, HEAD]
services.AddRestEaseApiFromConfiguration<IUserApi>(configuration, "Api");
// Caching block auto-wires into Polly pipeline
For code-only fields (KeyBuilder, ShouldCacheRequest, ShouldCacheResponse), layer .AddCaching(...) after config-binding:
services.AddRestEaseApiFromConfiguration<IUserApi>(configuration, "Api")
.AddCaching(c => c.KeyBuilder = req => $"v2:{req.RequestUri}");
Cache-Control semantics
- Request
Cache-Control: no-storeorno-cache→ bypass cache - Response
Cache-Control: no-store,no-cache, orprivate→ not stored - Response
Cache-Control: max-age=N→ TTL =min(N, MaxTtl ?? N) - No
Cache-Controlon response →DefaultTtl
Strategy ordering (inside Polly pipeline)
Pipeline order: Cache → RateLimiters → TotalTimeout → Retry → AttemptTimeout. Cache outermost — hit short-circuits all other strategies + the inner HTTP call. OAuth2 lives in the DelegatingHandler chain outside the Polly pipeline — cache hit skips token fetch automatically.
Key construction
Default key: KeyPrefix + "{METHOD} {ABSOLUTE-URL}".
Authorization header is excluded by default — same response served to every caller. Opt-in via IncludeAuthorizationInKey = true to isolate per-token (header value is SHA256-hashed, never stored raw).
Custom key:
.AddCaching(c => c.KeyBuilder = req =>
$"{req.Method.Method}:{req.RequestUri}:tenant={req.Headers.GetValues("X-Tenant").First()}");
Storage format
Each cache entry is a JSON envelope: status code, response headers, content headers, body bytes. Easy to inspect in Redis with GET key | jq.
Why IDistributedCache?
One abstraction covers in-process and out-of-process. IMemoryCache would lock you into a single replica. If you only need L1, AddDistributedMemoryCache is a MemoryDistributedCache — same in-memory performance, swappable later without code change. Two-tier (L1+L2) is out of scope here — use HybridCache if you need it and wire your own handler.
JSON
System.Text.Json is the only supported serializer. Defaults:
new JsonSerializerOptions(JsonSerializerDefaults.Web)
{
PropertyNameCaseInsensitive = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
Converters = { new JsonStringEnumConverter() }
};
Override globally per client:
.ConfigureJson(j =>
{
j.PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower;
j.Converters.Add(new MyCustomConverter());
});
Static Factory (non-DI)
using Dosaic.Extensions.RestEase;
// Simplest
var api = RestClientFactory.Create<IUserApi>("https://api.example.com");
// With OAuth2
var api = RestClientFactory.Create<IUserApi>("https://api.example.com", authConfig);
// With a custom resilience pipeline
var api = RestClientFactory.Create<IUserApi>("https://api.example.com", pipeline);
// Full configuration
var api = RestClientFactory.Create<IUserApi>("https://api.example.com", o =>
{
o.Timeout = TimeSpan.FromSeconds(20);
o.UserAgent = "my-service/1.0";
o.DefaultHeaders["X-Tenant"] = tenantId;
o.Authentication = authConfig;
o.ResiliencePipeline = pipeline;
o.JsonOptions = customJson;
});
The static factory builds a fresh handler chain (UserAgent → Resilience → OAuth2 → SocketsHttpHandler) per call. Prefer the DI path in long-running apps to benefit from IHttpClientFactory pooling.
Best Practices
- Use the DI path for hosted services. The static factory is for short-lived tools and tests.
- Call
.AddResilience()(or.AddCaching()/.AddRateLimits(), or enable equivalent option blocks) — auto-pipeline mounts unconditionally but adds zero strategies when no block is enabled. - One client interface per service. Don't reuse the same interface for two upstreams — register each with a distinct name.
- Set
RefreshSkew≥ 10 s so tokens refresh before they expire on the wire. Default is 30 s. - Distributed token cache: implement
ITokenProviderbacked by Redis/Vault when you run multiple replicas — otherwise each replica holds its own copy. - Don't combine
AddOAuth2with manualAuthorizationheaders on every request. If you must override per-call, setrequest.Headers.Authorization— the handler honours it. - Custom handlers stay stateless —
IHttpClientFactoryinstantiates them per request scope; per-handler state will surprise you. - Use the same client name + key everywhere —
AddRestEaseApi<TApi>(name)is keyed by name internally (options, token provider, http client all share it). - Override
JsonOptionsrather than rolling your own serializer — System.Text.Json is the only path supported.
API Reference
RestClientFactory (static)
| Method | Description |
|---|---|
Create<T>(string baseAddress) |
Default pipeline, no auth |
Create<T>(string baseAddress, AuthenticationConfig) |
Adds OAuth2 |
Create<T>(string baseAddress, ResiliencePipeline<HttpResponseMessage>) |
Replaces resilience pipeline |
Create<T>(string baseAddress, AuthenticationConfig, ResiliencePipeline<HttpResponseMessage>) |
Auth + custom pipeline |
Create<T>(string baseAddress, Action<StandaloneClientOptions>) |
Full options bag |
RestEaseDefaults
| Member | Description |
|---|---|
CreateDefaultJsonOptions() |
Web-mode STJ options w/ JsonStringEnumConverter |
CreateDefaultPipeline() |
Polly v8 retry + timeout pipeline (default for static factory) |
RestEaseClientOptions
| Property | Type | Description |
|---|---|---|
BaseAddress |
string |
API base URL |
Timeout |
TimeSpan? |
HttpClient.Timeout |
UserAgent |
string |
Appended to User-Agent header |
Authentication |
AuthenticationConfig |
OAuth2 settings |
JsonOptions |
JsonSerializerOptions |
Override STJ defaults |
DefaultHeaders |
Dictionary<string,string> |
Static request headers |
AuthenticationConfig
| Property | Type | Description |
|---|---|---|
Enabled |
bool |
Master switch |
BaseUrl |
string |
IdP base URL |
TokenUrlPath |
string |
Token endpoint path |
GrantType |
GrantType |
ClientCredentials · Password |
ClientId / ClientSecret |
string |
OAuth2 client identity |
Username / Password |
string |
Resource owner credentials |
Scope / Audience |
string |
Optional scope / audience |
RefreshSkew |
TimeSpan |
Refresh-buffer before real expiry. Default 30s |
ITokenProvider
public interface ITokenProvider
{
Task<AccessToken> GetTokenAsync(bool forceRefresh, CancellationToken cancellationToken);
void Invalidate();
}
public sealed class AccessToken
{
public string TokenType { get; init; }
public string Value { get; init; }
public DateTimeOffset ExpiresAt { get; init; }
}
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.Caching.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Caching.Memory (>= 10.0.8)
- Microsoft.Extensions.Configuration (>= 10.0.8)
- Microsoft.Extensions.DependencyInjection (>= 10.0.8)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Http (>= 10.0.8)
- Microsoft.Extensions.Http.Resilience (>= 10.1.0)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Options (>= 10.0.8)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.8)
- Newtonsoft.Json (>= 13.0.4)
- Polly (>= 8.6.5)
- RestEase (>= 1.6.4)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.2.34 | 89 | 6/10/2026 |
| 1.2.33 | 101 | 6/2/2026 |
| 1.2.31 | 102 | 5/28/2026 |
| 1.2.30 | 112 | 5/7/2026 |
| 1.2.29 | 108 | 5/5/2026 |
| 1.2.28 | 117 | 4/30/2026 |
| 1.2.27 | 102 | 4/29/2026 |
| 1.2.26 | 97 | 4/29/2026 |
| 1.2.25 | 116 | 4/27/2026 |
| 1.2.24 | 106 | 4/21/2026 |
| 1.2.23 | 119 | 4/14/2026 |
| 1.2.22 | 110 | 4/10/2026 |
| 1.2.21 | 106 | 4/10/2026 |
| 1.2.20 | 107 | 4/10/2026 |
| 1.2.19 | 110 | 4/9/2026 |
| 1.2.18 | 125 | 4/2/2026 |
| 1.2.17 | 109 | 4/1/2026 |
| 1.2.16 | 109 | 4/1/2026 |
| 1.2.15 | 108 | 3/31/2026 |
| 1.2.14 | 119 | 3/30/2026 |