Httpclient Factory

codewithmukesh/dotnet-claude-kit/skills/httpclient-factory

by codewithmukesh23300897f4d1No license754 starsListed Oct 8, 2026Updated Oct 8, 2026Repository updated 2 months ago

IHttpClientFactory and typed HTTP clients for .NET 10 applications. Covers named/typed/keyed clients, DelegatingHandlers, resilience with Microsoft.Extensions.Http.Resilience, and testing patterns. Load this skill when configuring HTTP clients, adding retry/circuit breaker policies, or when the user mentions "HttpClient", "IHttpClientFactory", "AddHttpClient", "typed client", "named client", "DelegatingHandler", "resilience", "retry", "circuit breaker", "hedging", "Polly", "AddStandardResilienceHandler", "socket exhaustion", or "Refit".

Instructions onlySoftware Development
AI-generated overview

Guidance for configuring IHttpClientFactory, typed and keyed HTTP clients, and resilience policies in .NET 10.

What it does
This skill provides reference guidance and code patterns for using IHttpClientFactory in .NET 10 applications. It covers named, typed, and keyed clients, DelegatingHandlers for auth and correlation IDs, resilience with Microsoft.Extensions.Http.Resilience, SocketsHttpHandler configuration, and testing with mock handlers. It also lists anti-patterns and a decision guide mapping scenarios to recommended approaches.
When to use it
Use it when configuring HTTP clients in .NET, adding retry, circuit breaker, timeout, or hedging policies, or when the user mentions HttpClient, IHttpClientFactory, AddHttpClient, typed or named clients, DelegatingHandler, resilience, Polly, or socket exhaustion.
Requirements
No scripts or packages ship with the skill; it is instructions only. Applying the patterns assumes a .NET 10 project and, for resilience features, the Microsoft.Extensions.Http.Resilience package.

HttpClient Factory

Core Principles

  1. Never new HttpClient() per request — Raw HttpClient creation causes socket exhaustion under load and ignores DNS changes. Use IHttpClientFactory to manage handler lifetimes.
  2. Keyed clients over typed clients — Keyed DI (.AddAsKeyed()) is the recommended pattern in .NET 10. Typed clients captured in singletons silently break handler rotation.
  3. Resilience is not optional — Every external HTTP call needs retry, circuit breaker, and timeout. AddStandardResilienceHandler() provides sensible defaults in one line.
  4. DelegatingHandlers for cross-cutting concerns — Auth tokens, correlation IDs, and logging belong in the handler pipeline, not scattered across service methods.

Patterns

Named Client with Resilience

csharp
builder.Services.AddHttpClient("github", client =>{    client.BaseAddress = new Uri("https://api.github.com/");    client.DefaultRequestHeaders.UserAgent.ParseAdd("MyApp/1.0");    client.DefaultRequestHeaders.Accept.Add(        new MediaTypeWithQualityHeaderValue("application/json"));}).AddStandardResilienceHandler();
// Usage via factorypublic sealed class GitHubService(IHttpClientFactory factory){    public async Task<Repo?> GetRepoAsync(string owner, string name, CancellationToken ct)    {        var client = factory.CreateClient("github");        return await client.GetFromJsonAsync<Repo>($"repos/{owner}/{name}", ct);    }}

Keyed Client (Recommended in .NET 10)

Combines named client configurability with direct injection. No string lookups.

csharp
builder.Services.AddHttpClient("payments", client =>{    client.BaseAddress = new Uri("https://api.payments.example.com/");}).AddStandardResilienceHandler().AddAsKeyed();  // Register as keyed scoped service
// Inject directly — no IHttpClientFactory neededapp.MapPost("/charge", async (    [FromKeyedServices("payments")] HttpClient httpClient,    ChargeRequest request,    CancellationToken ct) =>{    var response = await httpClient.PostAsJsonAsync("charges", request, ct);    return response.IsSuccessStatusCode        ? TypedResults.Ok()        : TypedResults.Problem("Payment failed");});

Global opt-in: builder.Services.ConfigureHttpClientDefaults(b => b.AddAsKeyed());

Standard Resilience Handler

AddStandardResilienceHandler() chains 5 strategies:

StrategyDefault
Rate limiter1000 concurrent requests
Total timeout30 seconds
Retry3 retries, exponential backoff with jitter
Circuit breakerOpens at 10% failure rate
Attempt timeout10 seconds per attempt
csharp
builder.Services.AddHttpClient("api")    .AddStandardResilienceHandler(options =>    {        options.Retry.MaxRetryAttempts = 5;        options.Retry.Delay = TimeSpan.FromSeconds(1);        options.TotalRequestTimeout.Timeout = TimeSpan.FromSeconds(60);        options.AttemptTimeout.Timeout = TimeSpan.FromSeconds(15);
        // Disable retries for non-idempotent methods        options.Retry.DisableForUnsafeHttpMethods();    });

DelegatingHandler for Auth Token Injection

csharp
public sealed class AuthenticationHandler(ITokenService tokenService)    : DelegatingHandler{    protected override async Task<HttpResponseMessage> SendAsync(        HttpRequestMessage request, CancellationToken cancellationToken)    {        var token = await tokenService.GetAccessTokenAsync(cancellationToken);        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);        return await base.SendAsync(request, cancellationToken);    }}
// Registrationbuilder.Services.AddTransient<AuthenticationHandler>();builder.Services.AddHttpClient("api")    .AddHttpMessageHandler<AuthenticationHandler>()    .AddStandardResilienceHandler();

DelegatingHandler for Correlation ID Propagation

csharp
public sealed class CorrelationIdHandler(IHttpContextAccessor httpContextAccessor)    : DelegatingHandler{    protected override Task<HttpResponseMessage> SendAsync(        HttpRequestMessage request, CancellationToken cancellationToken)    {        if (httpContextAccessor.HttpContext?.Request.Headers                .TryGetValue("X-Correlation-Id", out var correlationId) is true)        {            request.Headers.Add("X-Correlation-Id", correlationId.ToString());        }        return base.SendAsync(request, cancellationToken);    }}

SocketsHttpHandler Configuration

csharp
builder.Services.AddHttpClient("advanced")    .UseSocketsHttpHandler((handler, _) =>    {        handler.PooledConnectionLifetime = TimeSpan.FromMinutes(2);        handler.PooledConnectionIdleTimeout = TimeSpan.FromMinutes(1);        handler.MaxConnectionsPerServer = 100;        handler.AutomaticDecompression =            DecompressionMethods.GZip | DecompressionMethods.Brotli;    });

Testing with Mock Handler

csharp
public sealed class MockHttpHandler(    HttpStatusCode statusCode,    string content) : HttpMessageHandler{    protected override Task<HttpResponseMessage> SendAsync(        HttpRequestMessage request, CancellationToken cancellationToken)    {        return Task.FromResult(new HttpResponseMessage(statusCode)        {            Content = new StringContent(content, Encoding.UTF8, "application/json")        });    }}
// In testvar handler = new MockHttpHandler(HttpStatusCode.OK, """{"id":1}""");var client = new HttpClient(handler) { BaseAddress = new Uri("https://api.test/") };var service = new MyService(client);

Anti-patterns

Don't Create HttpClient Per Request

csharp
// BAD — socket exhaustion under load, ignores DNS changespublic async Task<string> GetDataAsync(){    using var client = new HttpClient();    return await client.GetStringAsync("https://api.example.com/data");}
// GOOD — factory-managedpublic async Task<string> GetDataAsync(CancellationToken ct){    var client = factory.CreateClient("api");    return await client.GetStringAsync("https://api.example.com/data", ct);}

Don't Capture Typed Clients in Singletons

csharp
// BAD — transient HttpClient captured by singleton defeats handler rotationservices.AddSingleton<MySingletonService>();services.AddHttpClient<MySingletonService>();
// GOOD — use keyed client or IHttpClientFactory in singletonsservices.AddSingleton<MySingletonService>();services.AddHttpClient("myservice").AddAsKeyed(ServiceLifetime.Singleton);

Don't Mutate DefaultRequestHeaders on Shared Clients

csharp
// BAD — not thread-safehttpClient.DefaultRequestHeaders.Authorization =    new AuthenticationHeaderValue("Bearer", token);
// GOOD — use DelegatingHandler or per-request HttpRequestMessageusing var request = new HttpRequestMessage(HttpMethod.Get, "/api/data");request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);await httpClient.SendAsync(request, ct);

Don't Forget CancellationToken

csharp
// BAD — no cancellation supportvar result = await httpClient.GetFromJsonAsync<Order>("/orders/1");
// GOOD — always pass CancellationTokenvar result = await httpClient.GetFromJsonAsync<Order>("/orders/1", cancellationToken);

Don't Stack Multiple Resilience Handlers

csharp
// BAD — conflicting resilience strategiesbuilder.AddStandardResilienceHandler();builder.AddStandardHedgingHandler();
// GOOD — one standard handler, or a custom pipelinebuilder.AddStandardResilienceHandler();

Decision Guide

ScenarioRecommendation
New .NET 10 projectKeyed clients with AddAsKeyed()
Singleton service needs HttpClientNamed client via IHttpClientFactory or keyed singleton
External API callsAddStandardResilienceHandler() on every client
Auth token injectionDelegatingHandler registered with AddHttpMessageHandler
Hedging (parallel requests)AddStandardHedgingHandler() for latency-sensitive calls
Non-idempotent methodsDisableForUnsafeHttpMethods() on retry options
Custom retry logicAddResilienceHandler("name", builder => ...)
Connection pooling controlUseSocketsHttpHandler with PooledConnectionLifetime
API client generationRefit with AddRefitClient<T>()
Integration testingCustom HttpMessageHandler or MockHttpMessageHandler

Source and attribution

Source:codewithmukesh/dotnet-claude-kitinskills/httpclient-factoryat commit2330089

License: No license

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal

More from codewithmukesh/dotnet-claude-kit

Wrap Up

codewithmukesh

Captures end-of-session work, pending tasks and learnings into a handoff file, and reloads it at session start.

Productivity & Workflow754updated 2 months ago

Workflow Mastery

codewithmukesh

Claude Code workflow mastery for .NET developers. Covers parallel execution with git worktrees, plan mode strategy, verification loops, auto-formatting hooks, permission setup for dotnet CLI, prompting techniques, subagent patterns, and context discipline — token budget management, MCP-first navigation, lazy loading, and subagent isolation — all adapted for the .NET ecosystem. Load this skill when setting up Claude Code for a .NET project, optimizing workflows, running parallel sessions, when context is running low or sessions feel sluggish, when exploring a large codebase efficiently, or when the user mentions "productivity", "workflow", "parallel", "worktree", "plan mode", "permissions", "hooks", "10x", "setup Claude Code", "speed up development", "context", "tokens", "budget", "running out of context", "too many files", or "large codebase". Inspired by tips from Boris Cherny (creator of Claude Code) and the Anthropic team.

Awaiting classification754updated 2 months ago

Vertical Slice

codewithmukesh

Guides .NET developers in structuring applications with Vertical Slice Architecture, covering feature folders, endpoint grouping and handler patterns.

Software Development754updated 2 months ago

Testing

codewithmukesh

Testing strategy for .NET 10 applications. Covers xUnit v3, WebApplicationFactory for integration tests, Testcontainers for real database testing, Verify for snapshot testing, and the AAA pattern. Load this skill when writing tests, setting up test infrastructure, reviewing test coverage, or when the user mentions "test", "xUnit", "WebApplicationFactory", "Testcontainers", "integration test", "unit test", "bUnit", "snapshot test", "Verify", "test coverage", "AAA pattern", "WireMock", or "FakeTimeProvider".

Awaiting classification754updated 2 months ago

Tdd

codewithmukesh

Guided test-driven development workflow for .NET 10 using xUnit v3, WebApplicationFactory, Testcontainers, and Verify snapshots. Follows the strict red-green-refactor cycle. Use when: "TDD", "test-driven", "let's TDD this", "red green refactor", "write the test first", or when building a feature with clear acceptance criteria.

Awaiting classification754updated 2 months ago

Spec

codewithmukesh

Turns a vague feature idea into an agreed, persisted specification file through structured questioning rounds.

Productivity & Workflow754updated 2 months ago