RA.Utilities.Integrations
RA.Utilities.Integrations simplifies and standardizes how you make HTTP calls to external APIs in .NET applications.
It builds on Microsoft's recommended IHttpClientFactory pattern and provides a reusable BaseHttpClient with all four HTTP verbs, strongly-typed query string and header parameters backed by a compile-time source generator, and a fluent registration pipeline.
🎯 Purpose
The package solves the problems that come up in every external API integration:
- Standardizes Configuration — centralize base URL, timeouts, and API keys in
appsettings.jsonviaBaseApiSettings<T>, keeping configuration out of your code. - Simplifies Registration — one extension method,
AddHttpClientIntegration, binds and validates configuration and registers a typedHttpClient. - Provides Compile-Time Safety —
[QueryParameters]and[HeaderParameters]classes are mapped to request parameters by a source generator, so typos in keys, wrong types, and missing mappings fail at build time instead of runtime. - Covers the Full CRUD Surface —
GetAsync,PostAsync,PutAsync, andDeleteAsyncshare one consistent request pipeline with JSON serialization built in. - Improves Resilience — typed clients via
IHttpClientFactoryplus optional logging and header-forwarding handlers for robust, observable integrations.
✨ Key Features
- Strongly-typed query strings —
[QueryParameters]+[QueryParameterName]attributes with generatedIQueryStringRequestimplementations - Strongly-typed headers —
[HeaderParameters]+[HeaderParameterName]attributes with generatedIHeaderRequestimplementations - Null-safe requests — query/header parameters are optional;
nullnever throws - JSON in, JSON out — bodies serialized as
application/json; charset=utf-8, responses deserialized with camel-case naming - Incremental source generator — shipped inside the package as an analyzer, zero reflection at runtime
🛠️ Getting Started
Prerequisites
- .NET 10.0 or later
- An ASP.NET Core application (the package references the
Microsoft.AspNetCore.Appshared framework)
Installation
.NET CLI
dotnet add package RA.Utilities.Integrations
Package Manager Console
Install-Package RA.Utilities.Integrations
PackageReference (csproj)
<PackageReference Include="RA.Utilities.Integrations" Version="10.2.0" />
Hello World
Define your parameter classes, build a client, and call an API:
using RA.Utilities.Integrations;
using RA.Utilities.Integrations.Attributes;
[QueryParameters]
public partial class GetProductsQuery
{
public int? CategoryId { get; init; }
public string? Search { get; init; }
}
[HeaderParameters]
public partial class RequestHeaders
{
[HeaderParameterName("x-request-id")]
public string? XCorrelationId { get; init; }
}
// Configured and injected via IHttpClientFactory; see ⚙️ Configuration below.
public class ProductsClient : BaseHttpClient
{
public ProductsClient(HttpClient httpClient, IOptions<IIntegrationSettings> settings)
: base(httpClient, settings)
{
}
public Task<Product?> GetProductsAsync(GetProductsQuery query) =>
GetAsync<Product>("products", query, new RequestHeaders { XCorrelationId = Guid.NewGuid().ToString() });
public Task<Product?> CreateProductAsync(Product product) =>
PostAsync<Product, Product>("products", product);
public Task<Product?> UpdateProductAsync(Product product) =>
PutAsync<Product, Product>($"products/{product.Id}", product);
public Task DeleteProductAsync(int id) =>
DeleteAsync($"products/{id}");
}
🧠 Core Concepts
The package is built from a few cooperating pieces:
| Component | Role | Example |
|---|---|---|
BaseHttpClient | One pipeline for GET, POST, PUT, and DELETE with JSON serialization | GetAsync<Product>("products", query) |
[QueryParameters] | Marker for strongly-typed query string classes (generator implements IQueryStringRequest) | ?CategoryId=5&Search=widgets |
[HeaderParameters] | Marker for strongly-typed header classes (generator implements IHeaderRequest) | x-request-id: trace-1 |
QueryUtilities | URL-encodes a QueryParams collection (skips empty values, ? prefix) | "?page=2" |
AddHttpClientIntegration | Registers a typed client, binds + validates settings | services.AddHttpClientIntegration<...>(section) |
BaseApiSettings<T> | Validated settings base class with strongly-typed endpoint names | BaseUrl, Timeout, Actions |
How they work together
appsettings.json ──► BaseApiSettings<T> ──► AddHttpClientIntegration ──► typed HttpClient
│
[QueryParameters] class ──(source generator)──► IQueryStringRequest ──────┤
[HeaderParameters] class ──(source generator)──► IHeaderRequest ──────────┤
▼
BaseHttpClient.GetAsync/PostAsync/PutAsync/DeleteAsync
│
┌─ QueryUtilities (URL encoding)
└─ JsonConverterUtilities (JSON body)
Terminology
- Strongly-typed parameters: Query/header values described as C# classes instead of
Dictionary<string, string>— property names are checked at compile time and surfaced by IntelliSense. - Source generator: A Roslyn incremental generator shipped inside the NuGet package (under
analyzers/) that emits partial class implementations at compile time. - Typed client: A class that receives a configured
HttpClientfromIHttpClientFactoryand exposes strongly-typed methods.
📖 How-To Guides
Call all four HTTP verbs
Product? product = await client.GetAsync<Product>(
"products/1",
queryString: new GetProductsQuery { CategoryId = 5 },
headers: new RequestHeaders { XCorrelationId = "trace-1" });
Product? created = await client.PostAsync<Product, Product>("products", product);
Product? updated = await client.PutAsync<Product, Product>("products/1", product);
await client.DeleteAsync("products/1");
Use repeated query keys and dictionaries
[QueryParameters]
public partial class FilterQuery
{
public int[] Ids { get; init; } = []; // ?Ids=1&Ids=2
public Dictionary<string, string> Filters { get; init; } // key of each entry becomes the query key
}
var query = new FilterQuery
{
Ids = [1, 2],
Filters = new() { ["brand"] = "acme" }
};
// GET /products?Ids=1&Ids=2&brand=acme
Override emitted keys and header names
[QueryParameters]
public partial class PagedQuery
{
[QueryParameterName("page_size")]
public int PageSize { get; init; }
}
[HeaderParameters]
public partial class RequestHeaders
{
[HeaderParameterName("x-request-id")]
public string? XCorrelationId { get; init; }
}
Register a typed client with validation
See the full walkthrough in AddHttpClientIntegration.
Add logging and header forwarding
services.AddHttpClientIntegration<IMyApiClient, MyApiClient, MyApiSettings>(configSection)
.AddLoggingHandler() // structured request/response logging
.AddInternalHeadersForwardingHandler(); // forwards Authorization + x-request-id per call
⚙️ Configuration
Settings live in appsettings.json and are bound + validated by AddHttpClientIntegration:
{
"Integrations": {
"MyApi": {
"BaseUrl": "https://api.example.com/v1/",
"Timeout": 60,
"Actions": {
"GetProduct": "products/{0}"
}
}
}
}
using RA.Utilities.Integrations.Extensions;
using RA.Utilities.Integrations.Options;
public class MyApiSettings : BaseApiSettings<MyApiActions> { }
public class MyApiActions
{
public string GetProduct { get; set; } = string.Empty;
}
services.AddHttpClientIntegration<IMyApiClient, MyApiClient, MyApiSettings>(
builder.Configuration.GetSection("Integrations:MyApi"));
BaseUrl and Actions are required and validated at startup (BaseApiSettings<T> details).
🔗 Dependencies
RA.Utilities.Core.ConstantsRA.Utilities.Logging.SharedMicrosoft.AspNetCore.Appshared framework reference
✨ Available Components
Abstractions
3 items
BaseHttpClient
The BaseHttpClient class serves as a reusable and configurable wrapper around .NET's HttpClient.
DelegatingHandler
2 items
Attributes
4 items
Models
1 item
Utilities
1 item
Migration Guides
v10.2.0 → v10.2.1
Troubleshooting & FAQ
Common Issues
RA.Utilities.Integrations.Extensions
3 items
RA.Utilities.Integrations.Options
1 item
🙌 Contributing & Support
Found a bug or have a suggestion?
- GitHub Issues: Report a bug or request a feature
- Discussions: Ask questions or start a discussion
Contributing
Contributions are welcome! Please follow the standard workflow:
- Fork the RA.Utilities repository
- Create a branch from
mainwith a descriptive name (e.g.,feature/new-integration-handler) - Make your changes — ensure XML doc comments are included for any new public API
- Submit a pull request to the
mainbranch with a clear description
Links
| Resource | URL |
|---|---|
| NuGet | RA.Utilities.Integrations |
| Source | GitHub — RedonAlla/RA.Utilities |
| License | MIT |
| Build Status | GitHub Actions |