Shaunebu.Common.RESTClient
1.0.1
dotnet add package Shaunebu.Common.RESTClient --version 1.0.1
NuGet\Install-Package Shaunebu.Common.RESTClient -Version 1.0.1
<PackageReference Include="Shaunebu.Common.RESTClient" Version="1.0.1" />
<PackageVersion Include="Shaunebu.Common.RESTClient" Version="1.0.1" />
<PackageReference Include="Shaunebu.Common.RESTClient" />
paket add Shaunebu.Common.RESTClient --version 1.0.1
#r "nuget: Shaunebu.Common.RESTClient, 1.0.1"
#:package Shaunebu.Common.RESTClient@1.0.1
#addin nuget:?package=Shaunebu.Common.RESTClient&version=1.0.1
#tool nuget:?package=Shaunebu.Common.RESTClient&version=1.0.1
π§ Shaunebu.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 all of this out-of-the-box, with zero extra setup. It is fully declarative, fully extensible, and completely aligned with how .NET developers already work.
π§ How It Works
RESTClient dynamically generates a strongly typed REST client at runtime based on an interface decorated with attributes.
Internally, the pipeline looks like this:
Interface
β Attribute Parser
β Proxy Builder
β Interceptor Pipeline
β Serialization & Content Negotiation
β Resilience Engine (Polly)
β HttpClient Transport
β Response Deserialization
Each part of this pipeline is extensible, customizable, and replaceable. This architecture allows RESTClient to provide:
per-method resilience
automatic serialization based on headers
observability hooks
request/response interceptors
fallback logic
caching
runtime validation
total control over HTTP behavior
with no boilerplate.
π― 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>() registers everything needed.
π 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
π§ Quick Start
public interface IPostsApi
{
[Get("/posts")]
Task<List<Post>> GetPostsAsync();
[Post("/posts")]
Task<Post> CreatePostAsync([Body] CreatePostRequest request);
}
// Dependency Injection
services.AddRESTClient<IPostsApi>("https://jsonplaceholder.typicode.com");
// Usage
var api = provider.GetRequiredService<IPostsApi>();
var result = await api.CreatePostAsync(new CreatePostRequest
{
Title = "Hello World",
Body = "RESTClient is amazing!"
});
β No configuration needed β serializers, resilience policies, interceptors, and handlers are automatically registered.
βοΈ Centralized Configuration
All global settings are managed through RESTClientOptions:
services.AddRESTClient(options =>
{
options.Timeout.RequestTimeoutSeconds = 30;
options.Resilience.RetryCount = 3;
options.Resilience.CircuitBreakerThreshold = 5;
options.DefaultCollectionFormat = CollectionFormat.Csv;
options.BufferResponse = true;
});
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] |
Declares HTTP methods |
[Body(Method = ...)] |
Selects serialization format (JSON, XML, MsgPack, etc.) |
[Query(Format=..., CollectionFormat=...)] |
Controls query parameter encoding |
[Host("https://...")] |
Overrides the default host per method |
[Fallback(typeof(MyFallback))] |
Defines a typed fallback provider |
[Retry], [Timeout], [CircuitBreaker], [Bulkhead] |
Method-level resilience controls |
[Headers], [Header], [AliasAs] |
Header customization |
[Multipart(Boundary="...")] |
Full multipart upload control |
[RateLimit], [HealthCheck] |
Advanced traffic and availability controls |
π Multi-Serialization and Content Negotiation
Shaunebu.RESTClient can serialize and deserialize payloads dynamically based on the Accept or Content-Type headers.
[Post("/users")]
Task<User> AddUser([Body(Method = BodySerializationMethod.Json)] User user);
[Post("/files/upload")]
[Multipart(Boundary = "----CustomBoundary")]
Task<ApiResponse<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.TryAddSingleton<IContentSerializer, MyCustomSerializer>();
π§ Dynamic Content Negotiation
The ContentNegotiator automatically chooses the correct serializer based on the requestβs Accept header.
Example:
[Get("/data")]
[Headers("Accept: application/x-protobuf")]
Task<MyResponse> GetDataInProtobuf();
The library will automatically:
use
ProtobufSerializerto serialize/deserialize the body,and send the correct
Content-Type.
πͺ Declarative Resilience (Polly Built-In)
Built-in attribute-level resilience, powered by Polly:
[Get("/data")]
[Retry(3, 2)] // Retries with exponential backoff
[Timeout(5)] // Timeout after 5 seconds
[CircuitBreaker(3, 10)] // Breaks circuit after 3 errors for 10s
Task<DataResponse> GetDataAsync();
No need to register Polly handlers manually β everything is wired automatically through DI.
π₯ Typed Fallback Providers
If a request fails, the fallback provider can return a typed result instead of throwing an exception.
[Get("/users/{id}")]
[Fallback(typeof(UserFallbackProvider))]
Task<User> GetUserAsync(int id);
public class UserFallbackProvider
{
public Task<User> GetFallbackAsync(Exception ex) =>
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>:
[Post("/upload")]
Task<ApiResponse> UploadLargeFileAsync([Body] 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.TryAddSingleton<IRESTInterceptor, MyLoggingInterceptor>();
Built-in Interceptors
π SecurityInterceptor
π§± ResilienceInterceptor
πͺ£ CachingInterceptor
π§ OpenTelemetryInterceptor
πͺ΅ LoggingInterceptor
π§ͺ DebugInterceptor
π₯ FallbackInterceptor
βοΈ PollyDebugInterceptor
π MetricsInterceptor
π§± Caching and Metrics
Out of the box support for:
ICacheStore(default:MemoryCacheStore)CachingInterceptor(automatic response caching)OpenTelemetry tracing (
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 | β | β |
[Retry], [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 |
β | β |
NoThrowPolicy for graceful error handling |
β | β |
| Source Generator & Proxy Fallback | β | β |
| 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))]
[Retry(3, 1)]
[Timeout(10)]
Task<User> GetUserAsync(int id);
[Post("/upload")]
[Multipart(Boundary = "----ShaunebuBoundary")]
Task<ApiResponse> 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.
πΊοΈ ** Roadmap **
| π Feature | π§© Status | π― Target Version | π ETA | π Notes |
|---|---|---|---|---|
| β Core Framework Stabilization | π’ Completed | v1.0.0 | Q4 2024 | Core system finalized β DI registration, interceptors, serializers, Polly, caching & fallbacks. |
| β Multi-Serialization Stack (JSON / XML / MessagePack / Protobuf) | π’ Completed | v1.0.0 | Q4 2024 | Content negotiation via headers, per-method [Body] serializer selection. |
| βοΈ Resilience Engine v2 | π‘ In Progress | v1.1.0 | Feb 2025 | Policy registry, adaptive retry & dynamic backoff support. |
| π Token Provider & Auth Interceptor | π‘ In Progress | v1.1.0 | Apr 2025 | OAuth / JWT auto-refresh through ITokenProvider. |
| πΎ Caching Enhancements v2 | π‘ In Progress | v1.1.0 | May 2025 | Redis / SQLite cache providers and configurable policies. |
| π§ RestClientMonitor (Dashboard & Telemetry) | π΅ Planned | v1.2.0 | Dec 2025 | π§© Embedded monitor endpoint (/_restclient/monitor) showing latency, retries, cache hits & failures. |
| πͺ΅ Structured Logging & Audit Trail | π΅ Planned | v1.2.0 | Dec 2025 | JSON log formatter + PII masking for GDPR compliance. |
| π§ OpenTelemetry Enricher Hooks | π΅ Planned | v1.2.0 | Jun 2025 | Custom span tags & metrics from RESTClient context. |
| βοΈ Dynamic Interceptor Pipeline | π΅ Planned | v1.3.0 | Aug 2025 | Add / remove interceptors dynamically at runtime. |
| π§ͺ In-Memory Mock Transport | π΅ Planned | v1.3.0 | Sep 2025 | Offline transport for testing (FakeHttpHandler). |
| πͺΆ Source Generator Integration | π΅ Planned | v1.4.0 | Nov 2025 | Build-time proxy generator via incremental Roslyn. |
| π CLI Tool for OpenAPI Import | π΅ Planned | v1.4.0 | Dec 2025 | dotnet restclient import openapi.json β automatic interface generation. |
| π§Ύ RESTClient Analyzer Package | π΅ Planned | v1.4.0 | Dec 2025 | Roslyn analyzers warning about missing attributes / timeouts. |
| π§± SignalR Integration | π΅ Planned | v1.5.0 | Q1 2026 | Bridge REST β SignalR with automatic message routing. |
π License
MIT Β© Shaunebu 2025
| 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.33.0)
- MessagePack (>= 3.1.4)
- MessagePack.Annotations (>= 3.1.4)
- Microsoft.Extensions.Caching.Abstractions (>= 9.0.10)
- Microsoft.Extensions.Caching.Memory (>= 9.0.10)
- Microsoft.Extensions.DependencyInjection (>= 9.0.10)
- Microsoft.Extensions.Http (>= 9.0.10)
- Microsoft.Extensions.Http.Polly (>= 9.0.10)
- Newtonsoft.Json (>= 13.0.4)
- Polly (>= 8.6.4)
- protobuf-net (>= 3.2.56)
-
net9.0
- Google.Protobuf (>= 3.33.0)
- MessagePack (>= 3.1.4)
- MessagePack.Annotations (>= 3.1.4)
- Microsoft.Extensions.Caching.Abstractions (>= 9.0.10)
- Microsoft.Extensions.Caching.Memory (>= 9.0.10)
- Microsoft.Extensions.DependencyInjection (>= 9.0.10)
- Microsoft.Extensions.Http (>= 9.0.10)
- Microsoft.Extensions.Http.Polly (>= 9.0.10)
- Newtonsoft.Json (>= 13.0.4)
- Polly (>= 8.6.4)
- 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.