Shaunebu.Common.RESTClient
1.0.9
dotnet add package Shaunebu.Common.RESTClient --version 1.0.9
NuGet\Install-Package Shaunebu.Common.RESTClient -Version 1.0.9
<PackageReference Include="Shaunebu.Common.RESTClient" Version="1.0.9" />
<PackageVersion Include="Shaunebu.Common.RESTClient" Version="1.0.9" />
<PackageReference Include="Shaunebu.Common.RESTClient" />
paket add Shaunebu.Common.RESTClient --version 1.0.9
#r "nuget: Shaunebu.Common.RESTClient, 1.0.9"
#:package Shaunebu.Common.RESTClient@1.0.9
#addin nuget:?package=Shaunebu.Common.RESTClient&version=1.0.9
#tool nuget:?package=Shaunebu.Common.RESTClient&version=1.0.9
π§ Shaunebu.Common.RESTClient
π Overview
Shaunebu.RESTClient is a next-generation, attribute-driven REST client framework for .NET β inspired by the simplicity of Refit, but engineered from the ground up to solve the challenges of real-world enterprise environments. While Refit focuses on mapping C# interfaces to REST endpoints, RESTClient goes beyond that philosophy:
π it provides a full SDK-generation platform, ready for production, observability, resilience, and extensibility.
Modern distributed systems require more than just making HTTP calls. They demand:
automatic retries, timeouts, and circuit breakers
telemetry for tracing and performance monitoring
multi-format serialization (JSON, XML, MessagePack, Protobuf)
typed fallbacks for offline or degraded operation
caching, logging, and security layers
pluggable interceptors
seamless DI integration
progress reporting for long-running uploads/downloads
RESTClient delivers these capabilities through a unified runtime pipeline, with public entry points for DI, standalone clients, generated clients, and dynamic clients.
It is declarative, extensible, and aligned with how .NET developers already configure HttpClient.
π§ How It Works
RESTClient can use either a source-generated client or a dynamic DispatchProxy client for an interface decorated with attributes.
Both paths now delegate to the same execution pipeline:
Generated Client ββ
ββ> IRESTRequestExecutor
Dynamic Client ββββ
β
RESTCachePipeline
β
IRESTRequestBuilder
β
Interceptors
β
IRESTResiliencePipeline
β
HttpClient.SendAsync
β
Interceptors
β
IRESTResponseProcessor
β
RESTReturnAdapter
Generated clients minimize per-call reflection and are the preferred path for trimming-sensitive applications. Dynamic clients remain available for runtime scenarios, but rely on DispatchProxy and reflection.
The unified pipeline provides:
per-method resilience
automatic serialization based on headers
observability hooks
request/response interceptors
fallback logic
caching
runtime validation
total control over HTTP behavior
Generated vs Dynamic
- Generated Client is selected automatically when the source generator emits an implementation for your interface. It delegates to
IRESTRequestExecutorand does not contain HTTP, serializer, fallback, cache, or response-processing logic. - Dynamic Client is used when no generated implementation is available or when you create one explicitly with
RESTClientImpl<T>.Create. It captures the exactMethodInfoand arguments, then delegates to the same executor. - DI and standalone clients use the same request builder, serializer resolver, response processor, resilience pipeline, cache pipeline, return adapter, options, and interceptors.
Pipeline Ownership
- Interceptors execute once per logical request from
IRESTRequestExecutor;RESTClientDelegatingHandleris retained only as a compatibility type and is no longer registered by default. - Fallback is evaluated by the executor for HTTP errors and pipeline exceptions. External cancellation does not execute fallback.
- Retry, timeout, circuit breaker, and bulkhead are coordinated by the internal resilience pipeline. Retries clone request messages and do not reuse a sent
HttpRequestMessage. - Cache is coordinated by the internal cache pipeline. Cache keys are deterministic SHA256 values and do not expose argument values in clear text.
StreamandHttpResponseMessageresults are not cached. - Streams and raw responses preserve ownership: returned
Stream/HttpResponseMessagevalues belong to the consumer; materialized responses are processed by the shared response processor.
Breaking Changes / Migration Notes
The unified pipeline preserves the main public consumer APIs, but changes which components participate in the default execution flow.
- Generated clients no longer own HTTP execution, serialization, fallback, cache, or response processing. They delegate to
IRESTRequestExecutor. - Dynamic clients use the same executor and should behave consistently with generated clients.
RESTClientDelegatingHandler,ClientHandler,ResilienceHandler,PolicyFactory,CachingInterceptor,FallbackInterceptor, andSuccessStatusCodeInterceptorare compatibility types. Do not use them for new application wiring.- Interceptors registered through DI run once per logical request from the executor.
- Relative routes now require a valid base address from
[Host]/absolute URI,HttpClient.BaseAddress, orRESTClientOptions.BaseAddress; otherwiseRESTClientConfigurationExceptionis thrown before send. - Dynamic clients remain reflection-based and are less trimming/AOT-friendly than generated clients.
See docs/MIGRATION-1.x.md for more detailed migration notes.
π― Design Principles
RESTClient is built on a clear philosophy:
1. Declarative by Default
Developers define behavior through attributes β not configuration files or boilerplate.
2. Production-Ready Out-of-the-Box
Retries, timeouts, logging, diagnostics, and telemetry are enabled automatically.
3. Extensible Everywhere
Interceptors, serializers, URL formatters, fallback providers β all pluggable.
4. Observable by Design
Built-in OpenTelemetry tracing, metrics, and structured logging support.
5. Safe Failure Behavior
Typed fallbacks and caching allow clients to operate gracefully under failure.
6. Multi-Format Native Support
JSON, XML, MessagePack, Protobuf, FormUrlEncoded, Multipart.
7. Zero Boilerplate
A single AddRESTClient<T>("https://api.example.com") registers the typed client, the configured HttpClient, and the unified pipeline.
π Why Shaunebu.RESTClient?
Refit provides a great abstraction for defining REST APIs as interfaces.
Shaunebu.RESTClient takes this philosophy and extends it into a complete, production-ready SDK framework.
- π§ Plug-and-play registration β just one line of DI setup.
- π§± Enterprise resilience stack β retries, circuit breakers, timeouts, fallback providers.
- π¦ Multi-format serialization β JSON, XML, MessagePack, Protobuf, UrlEncoded.
- π§© Interceptor pipeline β security, logging, caching, metrics, OpenTelemetry.
- π Dynamic content negotiation based on
Acceptheaders. - π₯ Fallback providers that return typed responses without throwing exceptions.
- β±οΈ Real upload/download progress tracking.
- βοΈ Extensible architecture (custom interceptors, serializers, negotiators).
π¦ Installation
dotnet add package Shaunebu.Common.RESTClient
| Package ID | Current project version |
|---|---|
Shaunebu.Common.RESTClient |
1.0.7 |
Supported frameworks
The package currently targets:
| Target Framework | Status |
|---|---|
net9.0 |
Supported |
net10.0 |
Supported |
Runtime dependencies
The package brings the runtime dependencies it needs through NuGet, including:
Microsoft.Extensions.DependencyInjectionMicrosoft.Extensions.HttpMicrosoft.Extensions.Caching.MemoryMicrosoft.Extensions.Http.PollyPollyNewtonsoft.JsonMessagePackGoogle.Protobuf/protobuf-net
π§ Quick Start
using Microsoft.Extensions.DependencyInjection;
using Shaunebu.Common.RESTClient.Abstractions;
using Shaunebu.Common.RESTClient.Attributes;
using Shaunebu.Common.RESTClient.Clients;
using Shaunebu.Common.RESTClient.Extensions;
using Shaunebu.Common.RESTClient.Interceptors;
public interface IPostsApi
{
[Get("/posts")]
Task<List<Post>> GetPostsAsync();
[Post("/posts")]
Task<Post> CreatePostAsync([Body] CreatePostRequest request);
}
public sealed class Post
{
public int Id { get; set; }
public int UserId { get; set; }
public string Title { get; set; } = "";
public string Body { get; set; } = "";
}
public sealed class CreatePostRequest
{
public int UserId { get; set; }
public string Title { get; set; } = "";
public string Body { get; set; } = "";
}
// Dependency Injection
var services = new ServiceCollection();
services.AddRESTClient<IPostsApi>("https://jsonplaceholder.typicode.com");
// Usage
using var provider = services.BuildServiceProvider();
var api = provider.GetRequiredService<IPostsApi>();
var result = await api.CreatePostAsync(new CreatePostRequest
{
UserId = 1,
Title = "Hello World",
Body = "RESTClient is amazing!"
});
β No manual handler wiring needed β serializers, resilience, cache, fallback, return adaptation, and interceptors are wired through the unified pipeline.
Official Sample
The official consumer sample lives in Shaunebu.Common.RESTClient.Sample. It demonstrates generated clients, dynamic clients, DI, standalone builder usage, RESTClientFactory, HTTP verbs, parameters, serializers, resilience, cache, fallback, interceptors, raw return types, IApiResponse<T>, and BaseAddress/host override behavior.
βοΈ Centralized Configuration
All global settings are managed through RESTClientOptions:
services.AddRESTClient(options =>
{
options.BaseAddress = new Uri("https://api.example.com");
options.Timeout.RequestTimeoutSeconds = 30;
options.Resilience.RetryCount = 3;
options.Resilience.CircuitBreakerThreshold = 5;
options.DefaultCollectionFormat = CollectionFormat.Csv;
options.BufferResponse = true;
});
Use services.AddRESTClient(options => ...) to register shared services and defaults. Use services.AddRESTClient<TClient>(baseAddress, options => ...) when you also want to register a typed client.
Client creation options
Use the public entry point that best fits your application:
// DI typed client. Uses a typed HttpClient configured with BaseAddress.
var services = new ServiceCollection();
services.AddRESTClient<IPostsApi>("https://jsonplaceholder.typicode.com");
using var provider = services.BuildServiceProvider();
var diClient = provider.GetRequiredService<IPostsApi>();
// Standalone generated/dynamic selection through the builder.
var standalone = RESTClientBuilder.ForStandalone<IPostsApi>(
"https://jsonplaceholder.typicode.com");
// Existing HttpClient. The builder does not replace this instance.
using var httpClient = new HttpClient
{
BaseAddress = new Uri("https://jsonplaceholder.typicode.com")
};
var generated = RESTClientBuilder.For<IPostsApi>(httpClient);
// Explicit dynamic client.
var dynamicClient = RESTClientImpl<IPostsApi>.Create(
httpClient,
new NullInterceptor());
// Factory from DI.
var factory = provider.GetRequiredService<IRESTClientFactory>();
var factoryClient = factory.CreateClient<IPostsApi>("https://jsonplaceholder.typicode.com");
AddRESTClient<TClient,TImplementation> is retained as a public overload for consumers that already use an explicit implementation type. The DI path still resolves the configured HttpClient through RESTClientBuilder.For<TClient>(httpClient, serviceProvider) so generated and dynamic clients use the same pipeline.
BaseAddress precedence
Relative routes such as [Get("/posts/{id}")] require an effective base address. The runtime resolves it consistently for generated and dynamic clients:
- Absolute URI in the method attribute or
[Host("https://...")]. HttpClient.BaseAddressconfigured for that client.RESTClientOptions.BaseAddress.RESTClientConfigurationExceptionbeforeHttpClient.SendAsyncif the request is still relative.
public interface IHostOverrideApi
{
[Get("/posts/{id}")]
[Host("https://jsonplaceholder.typicode.com")]
Task<Post> GetPostAsync(int id);
}
Example of appsettings.json registration
{
"RestClients": [
{
"Interface": "MyApp.Api.IPostsApi, MyApp",
"BaseUrl": "https://jsonplaceholder.typicode.com"
}
]
}
Then:
services.AddRESTClientsFromConfig(Configuration);
π§© Attribute Reference
| Attribute | Purpose |
|---|---|
[Get], [Post], [Put], [Delete], [Patch], [Head], [Options] |
Declares HTTP methods |
[Body], [Body(method: BodySerializationMethod.Xml)], [Body("text/plain")] |
Selects request body content type or built-in serialization mode |
[Query(Name = "...", CollectionFormat = ...)] |
Controls query parameter naming and collection encoding |
[Path("...")] |
Maps a parameter to a route placeholder when the CLR parameter name is not enough |
[Host("https://...")] |
Overrides the default host per method |
[Fallback(typeof(MyFallback))] |
Defines a typed fallback provider |
[RetryPolicy], [Timeout], [CircuitBreaker], [Bulkhead] |
Method-level resilience controls |
[Headers], [Header], [AliasAs] |
Header customization |
[Multipart], [Multipart("BoundaryValue")] |
Multipart upload control |
[RateLimit], [HealthCheck] |
Advanced traffic and availability controls |
Feature Coverage Quick Reference
This mirrors the scenarios exercised by the official sample project.
public interface ISampleApi
{
[Get("/posts/{id}")]
Task<Post> GetPostAsync(int id);
[Get("/posts")]
Task<List<Post>> GetPostsAsync([Query(Name = "userId")] int userId);
[Post("/posts")]
Task<Post> CreatePostAsync([Body] CreatePostRequest request);
[Put("/posts/{id}")]
Task<Post> UpdatePostAsync(int id, [Body] CreatePostRequest request);
[Patch("/posts/{id}")]
Task<Post> PatchPostAsync(int id, [Body] CreatePostRequest request);
[Delete("/posts/{id}")]
Task DeletePostAsync(int id);
[Head("/posts/{id}")]
Task<HttpResponseMessage> HeadPostAsync(int id);
[Options("/posts")]
Task<HttpResponseMessage> OptionsPostsAsync();
[Get("/comments")]
Task<List<Comment>> GetCommentsAsync(int? postId = null, int? id = null);
[Get("/posts")]
Task<List<Post>> GetPostsByIdsAsync(
[Query(Name = "id", CollectionFormat = CollectionFormat.Multi)] int[] ids);
[Get("/users/{id}")]
[Headers("X-Custom-Header: my-value")]
Task<IApiResponse<User>> GetUserWithDetailsAsync(int id, [Header("api-key")] string apiKey);
[Post("/post")]
[Multipart]
Task<string> UploadMultipartAsync(
[AliasAs("file")] StreamPart file,
[AliasAs("bytes")] byte[] bytes,
[AliasAs("description")] string description);
[Get("/posts/{id}")]
Task<string> GetPostAsStringAsync(int id);
[Get("/posts/{id}")]
Task<byte[]> GetPostAsBytesAsync(int id);
[Get("/posts/{id}")]
Task<Stream> GetPostAsStreamAsync(int id);
[Get("/posts/{id}")]
Task<HttpResponseMessage> GetRawPostAsync(int id);
}
π Multi-Serialization and Content Negotiation
Shaunebu.RESTClient can serialize and deserialize payloads dynamically based on body attributes, [Serializer], request content type, and response Content-Type headers.
public interface IUsersApi
{
[Post("/users")]
Task<User> AddUser([Body] User user);
[Post("/users/xml")]
Task<User> AddUserAsXml([Body(method: BodySerializationMethod.Xml)] User user);
[Post("/files/upload")]
[Multipart("ShaunebuBoundary")]
Task<string> UploadFile([AliasAs("file")] StreamPart file);
}
Supported Serializers
| Format | MIME Type | Implementation |
|---|---|---|
| JSON | application/json |
SystemTextJsonSerializer / NewtonsoftJsonSerializer |
| XML | application/xml |
XmlContentSerializer |
| MessagePack | application/x-msgpack |
MessagePackSerializer |
| Protobuf | application/x-protobuf |
ProtobufSerializer |
| FormUrlEncoded | application/x-www-form-urlencoded |
UrlEncodedContentSerializer |
You can register your own serializers:
services.AddSingleton<IContentSerializer, MyCustomSerializer>();
services.AddRESTClient<IMyApi>("https://api.example.com");
π§ Dynamic Content Negotiation
The ContentNegotiator resolves serializers from request content types, response Content-Type headers, [Serializer], and registered custom serializers. Accept can still be declared with [Headers] or [Accept] for APIs that negotiate response formats.
Example:
public interface IDataApi
{
[Get("/data")]
[Headers("Accept: application/x-protobuf")]
Task<MyResponse> GetDataInProtobuf();
}
The library will automatically:
use
ProtobufSerializerwhen the response content type resolves to protobuf,and use the configured serializer for matching request bodies.
πͺ Declarative Resilience (Polly Built-In)
Built-in attribute-level resilience, powered by Polly:
public interface IDataApi
{
[Get("/data")]
[RetryPolicy(retryCount: 3, sleepDurationInMs: 1000)]
[Timeout(timeoutInMs: 5000)]
[CircuitBreaker(exceptionsAllowedBeforeBreaking: 3, durationOfBreakInMs: 10000)]
Task<DataResponse> GetDataAsync();
}
No need to register Polly handlers manually β resilience is coordinated by the unified executor pipeline.
π₯ Typed Fallback Providers
If a request fails, the fallback provider can return a typed result instead of throwing an exception.
public interface IUsersApi
{
[Get("/users/{id}")]
[Fallback(typeof(UserFallbackProvider))]
Task<User> GetUserAsync(int id);
}
public class UserFallbackProvider : IFallbackProvider<User>
{
public Task<User> GetFallbackAsync(object[] args) =>
Task.FromResult(new User { Id = -1, Name = "Offline User" });
}
When triggered, the response includes a header:
X-Fallback-Executed: true
β±οΈ Progress Tracking
Real-time upload and download progress tracking using IProgress<double>:
public interface IUploadApi
{
[Post("/upload")]
[Multipart]
Task<string> UploadLargeFileAsync([AliasAs("file")] StreamPart file, IProgress<double> progress);
}
The progress value reports 0.0 β 1.0 (0% β 100%).
π Interceptors and Middleware
Interceptors allow you to hook into the entire request/response lifecycle.
public class MyLoggingInterceptor : IRESTInterceptor
{
public Task OnRequestAsync(RequestContext ctx)
{
Console.WriteLine($"β‘οΈ {ctx.Request.Method} {ctx.Request.RequestUri}");
return Task.CompletedTask;
}
public Task OnResponseAsync(ResponseContext ctx)
{
Console.WriteLine($"β¬
οΈ {ctx.Response.StatusCode}");
return Task.CompletedTask;
}
}
Add via DI:
services.AddSingleton<IRESTInterceptor, MyLoggingInterceptor>();
services.AddRESTClient<IMyApi>("https://api.example.com");
Default DI interceptors
π SecurityInterceptor
π AuthorizationInterceptor
π§ OpenTelemetryInterceptor
πͺ΅ LoggingInterceptor
π§ͺ DebugInterceptor
βοΈ PollyDebugInterceptor
π MetricsInterceptor
CachingInterceptor, FallbackInterceptor, ResilienceInterceptor, RESTClientDelegatingHandler, and related legacy handler types are retained for compatibility, but they are not the default execution path after the pipeline unification. Cache, fallback, and resilience are coordinated by the executor pipeline.
π§± Caching and Metrics
Out of the box support for:
ICacheStore(default:MemoryCacheStore)[Cache]through the internalRESTCachePipelineOpenTelemetry hooks (
OpenTelemetryInterceptor)Metrics gathering (
MetricsInterceptor)
π Full Feature Comparison
| Feature | Shaunebu RESTClient | Refit |
|---|---|---|
Unified DI Registration (AddRESTClient) |
β | β |
Centralized Config (RESTClientOptions) |
β | β οΈ |
| Content Negotiation | β | β |
| Multiple Serializers | β | β οΈ |
| Newtonsoft + System.Text.Json | β | β |
| Per-method Serializer | β | β |
| Accept Header Auto-Detection | β | β |
| Multipart Boundary Control | β | β οΈ |
| Upload/Download Progress | β | β |
[RetryPolicy], [Timeout], [CircuitBreaker] |
β | β |
| Typed Fallbacks | β | β |
| Polly Integration (Automatic) | β | β οΈ |
| RateLimit / Bulkhead / HealthCheck | β | β |
| Interceptors (Pipeline) | β | β |
| Built-in Logging & Telemetry | β | β οΈ |
| Response Caching | β | β |
| AppSettings Config Integration | β | β |
Query Formatting (CollectionFormat) |
β | β οΈ |
[Host] Attribute |
β | β |
IApiResponse<T> Support |
β | β |
Content-Type Aware Deserialization |
β | β |
ApiException<T> and typed fallback support |
β | β οΈ |
| Source generator + dynamic proxy client paths | β | β |
| Production-Ready Stack | β | β |
π§ Deep Comparison with Refit
While Refit is excellent for lightweight API calls, it has limitations in enterprise scenarios:
Refit is ideal for:
Simple REST wrappers
Mobile apps
Low-infrastructure projects
Rapid prototypes
RESTClient is designed for:
Microservices
Enterprise APIs
SDK development
Mission-critical integrations
Highly observable systems
Environments where resiliency is mandatory
In short:
Refit helps you call APIs β RESTClient helps you build SDKs for APIs.
π§© Extending Shaunebu.RESTClient
Custom Serializer
Implement IContentSerializer to plug in any serialization logic.
Custom Interceptor
Add your own interceptor for metrics, logging, or security checks.
Custom Fallback Provider
Attach to [Fallback(typeof(...))] for custom recovery logic.
Custom Url Parameter Formatter
Implement IUrlParameterFormatter to change query escaping globally.
π§© Example: Complex Client Definition
[Headers("Authorization: Bearer")]
[Host("https://api.override.com")]
public interface IComplexApi
{
[Get("/users/{id}")]
[Fallback(typeof(UserFallback))]
[RetryPolicy(retryCount: 3, sleepDurationInMs: 1000)]
[Timeout(timeoutInMs: 10000)]
Task<User> GetUserAsync(int id);
[Post("/upload")]
[Multipart("ShaunebuBoundary")]
Task<string> UploadFileAsync([AliasAs("file")] StreamPart file, IProgress<double> progress);
}
π§° Recommended Use Cases
π§± SDK Generation for enterprise APIs
βοΈ Microservice Clients with per-method resiliency
π§© Highly observable integrations (with OpenTelemetry)
π Data ingestion services requiring metrics and caching
πΎ Offline-capable APIs using fallbacks and memory cache
π§© Credits
Originally inspired by Refit, but re-engineered by Jorge Perales DΓaz
for real-world, large-scale systems that need reliability, observability, and full-stack resilience.
πΊοΈ Current Status
| Capability | Status | Notes |
|---|---|---|
| Unified generated + dynamic pipeline | β Completed | Both paths delegate to IRESTRequestExecutor. |
| Source generator integration | β Completed | Generated clients are preferred for trimming-sensitive applications. |
Dynamic DispatchProxy client |
β Completed | Available through RESTClientImpl<T>.Create and fallback creation paths. |
| DI / standalone / factory creation | β Completed | AddRESTClient, RESTClientBuilder, and RESTClientFactory share the same executor path. |
| JSON, XML, URL encoded, MessagePack, Protobuf, custom serializers | β Completed | Serializer resolution is centralized through ISerializerResolver and ContentNegotiator. |
| Retry, timeout, circuit breaker, bulkhead, cache, fallback | β Completed | Coordinated by the unified pipeline and method attributes. |
| Memory cache store | β Completed | MemoryCacheStore is registered by default. |
| Distributed cache adapter | β Available | DistributedCacheStore exists, but Redis/SQLite providers are not bundled here. |
| Dashboard, CLI import, analyzer package, SignalR bridge | π§ Not included | Do not rely on these as current runtime features. |
π License
MIT Β© Shaunebu 2026
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. 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
- Google.Protobuf (>= 3.35.1)
- MessagePack (>= 3.1.8)
- MessagePack.Annotations (>= 3.1.8)
- Microsoft.Extensions.Caching.Abstractions (>= 10.0.10)
- Microsoft.Extensions.Caching.Memory (>= 10.0.10)
- Microsoft.Extensions.DependencyInjection (>= 10.0.10)
- Microsoft.Extensions.Http (>= 10.0.10)
- Microsoft.Extensions.Http.Polly (>= 10.0.10)
- Newtonsoft.Json (>= 13.0.4)
- Polly (>= 8.7.0)
- protobuf-net (>= 3.2.56)
-
net9.0
- Google.Protobuf (>= 3.35.1)
- MessagePack (>= 3.1.8)
- MessagePack.Annotations (>= 3.1.8)
- Microsoft.Extensions.Caching.Abstractions (>= 10.0.10)
- Microsoft.Extensions.Caching.Memory (>= 10.0.10)
- Microsoft.Extensions.DependencyInjection (>= 10.0.10)
- Microsoft.Extensions.Http (>= 10.0.10)
- Microsoft.Extensions.Http.Polly (>= 10.0.10)
- Newtonsoft.Json (>= 13.0.4)
- Polly (>= 8.7.0)
- protobuf-net (>= 3.2.56)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.