Httpclient Factory

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

作者 codewithmukesh23300897f4d1無授權條款754 個星標收錄於 2026年10月8日更新於 2026年10月8日儲存庫2 個月前更新

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".

AI 產生的概覽

關於在 .NET 10 中設定 IHttpClientFactory、型別化與鍵控 HTTP 用戶端及復原策略的指引。

功能
此技能提供在 .NET 10 應用程式中使用 IHttpClientFactory 的參考指引與程式碼模式。內容涵蓋具名、型別化與鍵控用戶端,用於注入驗證權杖與關聯識別碼的 DelegatingHandler,以 Microsoft.Extensions.Http.Resilience 為基礎的復原策略,SocketsHttpHandler 設定,以及使用模擬處理常式進行測試。它也列出反模式,並提供將情境對應到建議做法的決策指南。
適用情境
適用於在 .NET 中設定 HTTP 用戶端、加入重試、斷路器、逾時或對沖策略時,或當使用者提到 HttpClient、IHttpClientFactory、AddHttpClient、型別化或具名用戶端、DelegatingHandler、復原、Polly 或通訊端耗盡時。
執行需求
此技能不隨附指令碼或套件,僅為說明性內容。套用這些模式需要 .NET 10 專案,使用復原功能還需要 Microsoft.Extensions.Http.Resilience 套件。

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

來源與署名

來源:codewithmukesh/dotnet-claude-kit位於skills/httpclient-factory提交2330089

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架

更多來自 codewithmukesh/dotnet-claude-kit 的技能

Wrap Up

codewithmukesh

在 session 結束時把已完成工作、待辦事項與經驗寫入交接檔案,並在 session 開始時重新載入。

Productivity & Workflow7542 個月前更新

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.

待分類7542 個月前更新

Vertical Slice

codewithmukesh

指導 .NET 開發者以垂直切片架構組織應用程式,涵蓋功能資料夾、端點分組與處理常式模式。

Software Development7542 個月前更新

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".

待分類7542 個月前更新

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.

待分類7542 個月前更新

Spec

codewithmukesh

透過結構化提問,把模糊的功能想法轉化為雙方確認並持久化的規格文件。

Productivity & Workflow7542 個月前更新