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 的参考指导与代码模式。内容涵盖命名、类型化和键控客户端,用于注入身份验证令牌和关联 ID 的 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

在会话结束时把已完成工作、待办任务与经验写入交接文件,并在会话开始时重新载入。

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个月前更新