MisterMoret.Http 1.0.0-beta.10

This is a prerelease version of MisterMoret.Http.
dotnet add package MisterMoret.Http --version 1.0.0-beta.10
                    
NuGet\Install-Package MisterMoret.Http -Version 1.0.0-beta.10
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="MisterMoret.Http" Version="1.0.0-beta.10" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="MisterMoret.Http" Version="1.0.0-beta.10" />
                    
Directory.Packages.props
<PackageReference Include="MisterMoret.Http" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add MisterMoret.Http --version 1.0.0-beta.10
                    
#r "nuget: MisterMoret.Http, 1.0.0-beta.10"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package MisterMoret.Http@1.0.0-beta.10
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=MisterMoret.Http&version=1.0.0-beta.10&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=MisterMoret.Http&version=1.0.0-beta.10&prerelease
                    
Install as a Cake Tool

MisterMoret.Http

License: MIT NuGet .NET 8.0 .NET 9.0 .NET 10.0 NuGet GitHub

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 structured HttpResult error 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 CancellationToken as 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 custom IAccessTokenProvider.
  • 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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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