MisterMoret.Http
1.0.0-beta.10
dotnet add package MisterMoret.Http --version 1.0.0-beta.10
NuGet\Install-Package MisterMoret.Http -Version 1.0.0-beta.10
<PackageReference Include="MisterMoret.Http" Version="1.0.0-beta.10" />
<PackageVersion Include="MisterMoret.Http" Version="1.0.0-beta.10" />
<PackageReference Include="MisterMoret.Http" />
paket add MisterMoret.Http --version 1.0.0-beta.10
#r "nuget: MisterMoret.Http, 1.0.0-beta.10"
#:package MisterMoret.Http@1.0.0-beta.10
#addin nuget:?package=MisterMoret.Http&version=1.0.0-beta.10&prerelease
#tool nuget:?package=MisterMoret.Http&version=1.0.0-beta.10&prerelease
MisterMoret.Http
A simple and extensible API client wrapper for .NET, built on top of IHttpClientFactory and integrated with MisterMoret.Results for robust error handling and status code management.
This package is currently in beta and available via NuGet.org.
✨ Features
- Named API Clients: Easily register and use multiple API clients via
IApiClientFactory. - Typed Responses: Automatic JSON (de)serialization into typed objects.
- Result Pattern Integration: Returns
HttpResult<T>instead of throwing exceptions for non-success status codes. When the server returns a structuredHttpResulterror body, its errors are surfaced directly; otherwise a generic failure is returned. - Query Parameter Support: Simplified way to pass query parameters via anonymous objects or classes.
- Cancellation Support: Every HTTP verb method accepts an optional
CancellationTokenas its last parameter. - Authentication Support: Built-in bearer token injection via
IAccessTokenProvider, designed for global/machine tokens (e.g. client credentials, API keys). Extensible for per-user scenarios via a customIAccessTokenProvider. - Configurable Options: Control base address, timeout, and user-agent through
ApiClientOptions. - Dependency Injection Ready: Seamlessly integrates with
IServiceCollection. - Modern .NET Support: Targets .NET 8.0, 9.0, and 10.0.
🚀 Installation
Install the package via the NuGet CLI:
dotnet add package MisterMoret.Http --version 1.0.0-beta.10
💡 Usage
1. Registration
Register your API client in Program.cs or Startup.cs:
using MisterMoret.Http.Extensions;
var builder = WebApplication.CreateBuilder(args);
// Register a named client
builder.Services.AddApiClient("MyService", options =>
{
options.BaseAddress = "https://api.example.com/v1/";
options.Timeout = TimeSpan.FromSeconds(30); // optional, default is 100s
options.UserAgent = "MyApp/1.0"; // optional
});
// Register a default (unnamed) client
builder.Services.AddApiClient(options =>
{
options.BaseAddress = "https://api.example.com/v1/";
});
2. Basic Usage
Inject IApiClientFactory and create a client by name:
using MisterMoret.Http;
public class MyService
{
private readonly IApiClientFactory _apiClientFactory;
public MyService(IApiClientFactory apiClientFactory)
{
_apiClientFactory = apiClientFactory;
}
public async Task<string?> GetUserName(int userId, CancellationToken cancellationToken = default)
{
var client = _apiClientFactory.CreateClient("MyService");
// Returns an HttpResult<User>
var result = await client.GetAsync<User>($"users/{userId}", cancellationToken);
if (result.IsSuccess)
{
return result.Value.Name;
}
// Handle failure — result.Errors contains messages from the server's error body
// if it returned a structured HttpResult, or a generic fallback message otherwise.
return null;
}
}
When using the default client, call CreateClient() without arguments:
var client = apiClientFactory.CreateClient();
3. POST/PUT with JSON
Use the two-type-parameter overloads when the server returns a response body you want to deserialize:
var newUser = new User { Name = "John Doe" };
HttpResult<User> result = await client.PostAsync<User, User>("users", newUser);
if (result.IsSuccess)
{
var created = result.Value;
}
Use the single-type-parameter overloads when no response body is expected:
var updated = new User { Name = "Jane Doe" };
// POST — returns HttpResult (no response body)
HttpResult postResult = await client.PostAsync<User>("users/1/deactivate", updated);
// PUT — returns HttpResult (no response body)
HttpResult putResult = await client.PutAsync<User>("users/1", updated);
if (postResult.IsSuccess)
{
// Accepted, nothing to deserialize
}
4. PATCH
Use the two-type-parameter overload when the server returns a response body:
var patch = new UserPatch { Name = "Jane Doe" };
HttpResult<User> result = await client.PatchAsync<UserPatch, User>("users/1", patch);
if (result.IsSuccess)
{
var patched = result.Value;
}
Use the single-type-parameter overload when no response body is expected:
HttpResult result = await client.PatchAsync<UserPatch>("users/1", patch);
if (result.IsSuccess)
{
// Accepted, nothing to deserialize
}
5. POST with Raw Content
Use the HttpContent overload to post non-JSON content, such as a multipart form upload:
using var stream = File.OpenRead("photo.jpg");
using var multipart = new MultipartFormDataContent();
multipart.Add(new StreamContent(stream), "file", "photo.jpg");
var result = await client.PostAsync<UploadResponse>("users/1/photo", multipart);
The part name passed to Add (here "file") must match the IFormFile parameter name on the receiving server action.
6. GET with Query Parameters
var query = new { Search = "Frédéric", Page = 1 };
var result = await client.GetAsync<List<User>, object>("users", query);
7. Authentication
Pass an authentication scheme when registering a client to enable bearer token injection:
builder.Services.AddApiClient("MyService", options =>
{
options.BaseAddress = "https://api.example.com/v1/";
}, "Bearer");
This registers IAccessTokenProvider as a singleton service. Inject it wherever you obtain a token and store it for the client:
using MisterMoret.Http.Authentication;
public class AuthService
{
private readonly IAccessTokenProvider _tokenProvider;
public AuthService(IAccessTokenProvider tokenProvider)
{
_tokenProvider = tokenProvider;
}
public void StoreToken(string token)
{
// For a specific named client
_tokenProvider.SetAccessToken("MyService", token);
// Or globally, for clients registered without a name
_tokenProvider.SetAccessToken(token);
}
}
The AuthenticationHandler automatically reads the token and attaches it as an Authorization header on every outgoing request for that client.
Built-in AccessTokenProvider — intended use cases
The built-in AccessTokenProvider stores one token per named client (plus one global token for the default client) for the lifetime of the application. This makes it suitable for:
- Machine-to-machine authentication — a single shared token for all requests (e.g. client credentials OAuth flow, API key)
- Desktop / mobile apps (e.g. MAUI) — one logged-in user for the entire app session
- Background services / workers — a service account token set once at startup or refreshed by a background task
Per-user scenarios (MVC, Blazor) — use a custom IAccessTokenProvider
The built-in implementation is not suitable for web apps where each user has their own token. Because it is a singleton with a single token per client, tokens from concurrent requests would overwrite each other, causing users to send each other's tokens.
For per-user scenarios, implement IAccessTokenProvider to read the token from the current HTTP context and register it before calling AddApiClient (so TryAddSingleton skips the built-in one):
// In MVC — reads the access token stored in the authentication cookie
public class UserAccessTokenProvider : IAccessTokenProvider
{
private readonly IHttpContextAccessor _httpContextAccessor;
public UserAccessTokenProvider(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public string? GetAccessToken() =>
_httpContextAccessor.HttpContext?
.GetTokenAsync("access_token")
.GetAwaiter().GetResult();
public string? GetAccessToken(string clientName) => GetAccessToken();
public void SetAccessToken(string accessToken) { }
public void SetAccessToken(string clientName, string accessToken) { }
}
// Register before AddApiClient so TryAddSingleton skips the built-in implementation
builder.Services.AddSingleton<IAccessTokenProvider, UserAccessTokenProvider>();
builder.Services.AddApiClient("MyService", options =>
{
options.BaseAddress = "https://api.example.com/v1/";
}, "Bearer");
In Blazor Server, IHttpContextAccessor is unreliable inside components after the initial HTTP handshake (the connection switches to SignalR). Capture the token during the initial request and store it in a scoped service instead.
In Blazor WebAssembly, there is no server-side HTTP context. Tokens are managed in the browser and typically obtained via IAccessTokenProvider from Microsoft.AspNetCore.Components.WebAssembly.Authentication, which you can wrap.
⚖️ License
This project is licensed under the MIT License - see the LICENSE file for details.
👤 Author
Frédéric Goetinck-Moret
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. 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
- Microsoft.AspNetCore.WebUtilities (>= 8.0.27)
- Microsoft.Extensions.Http (>= 10.0.8)
- MisterMoret.Results (>= 1.0.0-beta.5)
-
net8.0
- Microsoft.AspNetCore.WebUtilities (>= 8.0.27)
- Microsoft.Extensions.Http (>= 10.0.8)
- MisterMoret.Results (>= 1.0.0-beta.5)
- System.Net.Http.Json (>= 10.0.8)
-
net9.0
- Microsoft.AspNetCore.WebUtilities (>= 8.0.27)
- Microsoft.Extensions.Http (>= 10.0.8)
- MisterMoret.Results (>= 1.0.0-beta.5)
- System.Net.Http.Json (>= 10.0.8)
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.0.0-beta.10 | 85 | 6/3/2026 |
See the full changelog at https://github.com/fgoetinck/mister-moret/blob/main/src/MisterMoret.Http/CHANGELOG.md