Chapter 33: Calling remote APIs with IHttpClientFactory
Introduces consuming remote HTTP APIs in ASP.NET Core and previews IHttpClientFactory, resilience policies, and custom handlers for reliable HTTP calls.
After this chapter you will be able to
- IHttpClientFactory chapter roadmap. Describe the chapter's approach to HTTP calls: IHttpClientFactory manages HttpClient creation and handler lifetimes, while named and typed clients centralize configuration and encapsulate a remote service's calls.
- Per-request disposal socket exhaustion. Explain why creating an HttpClient in a using statement for each call is incorrect: disposing it does not immediately free its underlying socket, because the TCP/IP connection remains in TIME_WAIT. Trace how many rapid new connections can consume available ports, causing socket exhaustion and making the application wait for a socket.
- Singleton client DNS staleness. Explain that a singleton HttpClient avoids socket exhaustion by reusing its connection and socket, but it normally does not perform another DNS lookup while that original connection exists. Describe why this means it can continue calling an old IP address after a DNS change used for cloud load balancing or a rollout, until the original connection closes.
- IHttpClientFactory purpose. Explain that IHttpClientFactory helps an application create HttpClient instances correctly and avoid the common faulty HttpClient approaches discussed previously. Describe that it also supports configuring multiple clients and an outgoing-request middleware pipeline.
- HttpClient creation patterns. Describe the three IHttpClientFactory patterns previewed here: calling CreateClient() as a drop-in replacement, using named clients for a particular third-party API, and using typed clients to encapsulate calls to a third-party API.
- Handler pooling and rotation. Explain how IHttpClientFactory separates HttpClient lifetime from HttpClientHandler lifetime by pooling handlers and rotating the active handler every two minutes. Relate handler reuse to avoiding socket exhaustion, and handler rotation plus eventual disposal to making new connections that respect DNS changes.
- Factory registration and client creation. Describe how to add IHttpClientFactory to application services and inject the singleton factory into another service, then call CreateClient() to obtain an HttpClient backed by a pooled handler. Explain why this is a low-change refactoring pattern: normally only HttpClient creation code changes.
- Named client configuration. Explain how the Named Client pattern registers a string-named HttpClient with IHttpClientFactory and runs its configuration function whenever that named client is requested. Describe how this puts shared settings such as BaseAddress and default request headers in one central registration location.
- Typed client encapsulation. Explain how a typed client accepts an HttpClient in its constructor and exposes application-specific methods that hide the remote API's URLs, HTTP verbs, and response types from its consumers.
- Typed client factory registration. Describe how registering AddHttpClient<ExchangeRatesClient> makes IHttpClientFactory create a configured pooled HttpClient and inject it when DI creates a new transient ExchangeRatesClient.
- Transient HTTP error handling. Explain what a transient HTTP error is and why retrying a request can sometimes succeed. Describe how temporary network failures or a temporarily unavailable remote API differ from a permanent failure.
- Polly handler integration. Describe how Microsoft.Extensions.Http.Polly integrates Polly into an IHttpClientFactory-created named or typed client as an HttpMessageHandler. State that the handler applies its policy automatically to requests made by that configured HttpClient.
- Retry policy behavior. Describe the sample retry policy: it retries transient failures three times with an increasing delay between attempts, then returns the final failure to the client if the third retry fails. State the default failures it handles: HttpRequestException, HTTP 5xx responses, and HTTP 408 responses.
- Custom handler pipeline. Explain how a custom HttpMessageHandler participates in an HttpClient handler pipeline. Describe how it can modify an outgoing request, pass it to the next handler, and later inspect or modify the incoming response.
- Handler implementation and registration. Describe the steps for adding a custom handler to a named or typed client: derive from DelegatingHandler, override SendAsync and call base.SendAsync, register the handler in dependency injection, and attach it with AddHttpMessageHandler<T>(). Explain how DI lifetime and registration order affect the handler.
What it builds on
- What is an HTTP API, and when should you use one? (chapter 5) · The idea of HTTP APIs and applications communicating over HTTP.
- Creating your first web API project (chapter 20) · Experience creating an HTTP API project, which this chapter contrasts with consuming APIs.
- Using dependency injection in ASP.NET Core (chapter 8) · Using dependency injection in ASP.NET Core.
- Understanding lifetimes: When are services created? (chapter 9) · The significance of service lifetimes.
- Defining middleware (chapter 4) · The general idea of middleware as components that run common request-processing behavior.
- Using services from the DI container (chapter 8) · Using registered services through dependency injection.
- Singleton: There can be only one (chapter 9) · The singleton service lifetime.
- Transient: Everyone is unique (chapter 9) · The meaning of a transient service lifetime.
Section by section
Introduction
Introduces consuming remote HTTP APIs in ASP.NET Core and previews IHttpClientFactory, resilience policies, and custom handlers for reliable HTTP calls.
- Remote API consumption context: Explain why an ASP.NET Core application may need to consume remote HTTP APIs as well as expose its own APIs, using examples such as payments, email, SMS, or exchange-rate services.
Why it matters: Most production applications depend on external services, not just the endpoints they expose themselves. - IHttpClientFactory chapter roadmap: Describe the chapter's approach to HTTP calls: IHttpClientFactory manages HttpClient creation and handler lifetimes, while named and typed clients centralize configuration and encapsulate a remote service's calls.
Why it matters: This roadmap connects the factory's lifetime management with organized, reusable client code for remote services. - Transient error resilience: Explain that network glitches and other transient HTTP errors should be handled gracefully, and that Polly provides retry policies as well as the possibility of more complex resilience policies.
Why it matters: Handling temporary failures prevents a brief network problem from immediately becoming an application failure. - Cross-cutting request handlers: Describe how custom HttpMessageHandler handlers managed by IHttpClientFactory can run cross-cutting behavior on every outgoing HTTP call, including logging, metrics, authentication, or automatically adding an API key.
Why it matters: Handlers let an application apply common outbound-request behavior consistently instead of repeating it at every call site.
Key terms: HTTP APIs, HttpClient, third-party services, remote APIs, IHttpClientFactory, HttpClientHandler, handler pipeline, named clients, typed clients, transient errors
33.1 Calling HTTP APIs: The problem with HttpClient
This section explains why creating and disposing HttpClient per request causes socket exhaustion, while keeping one singleton client can miss DNS changes, motivating IHttpClientFactory.
- HttpClient API calls: Describe HttpClient as the .NET class used to call HTTP APIs by sending request headers and bodies and reading response headers and data. Explain why applications commonly call third-party HTTP services instead of implementing specialized capabilities such as payments or email themselves.
Why it matters: Outbound HTTP calls are how an ASP.NET Core application commonly integrates with external services. - Per-request disposal socket exhaustion: Explain why creating an HttpClient in a using statement for each call is incorrect: disposing it does not immediately free its underlying socket, because the TCP/IP connection remains in TIME_WAIT. Trace how many rapid new connections can consume available ports, causing socket exhaustion and making the application wait for a socket.
Why it matters: A seemingly standard IDisposable pattern can make a busy application unstable and unable to make outbound requests. - Singleton client DNS staleness: Explain that a singleton HttpClient avoids socket exhaustion by reusing its connection and socket, but it normally does not perform another DNS lookup while that original connection exists. Describe why this means it can continue calling an old IP address after a DNS change used for cloud load balancing or a rollout, until the original connection closes.
Why it matters: Reusing one client fixes the port problem but can send traffic to an outdated service instance after infrastructure changes.
Key terms: HttpClient, HTTP API, RESTful HTTP services, IDisposable, using statement, socket, socket exhaustion, TIME_WAIT, singleton HttpClient, DNS
33.2 Creating HttpClients with IHttpClientFactory
This section introduces IHttpClientFactory as the safer way to create and configure HttpClient instances, previewing default, named, and typed client patterns.
- IHttpClientFactory purpose: Explain that IHttpClientFactory helps an application create HttpClient instances correctly and avoid the common faulty HttpClient approaches discussed previously. Describe that it also supports configuring multiple clients and an outgoing-request middleware pipeline.
Why it matters: Using the factory provides a central, safer foundation for making calls to remote HTTP APIs. - HttpClient creation patterns: Describe the three IHttpClientFactory patterns previewed here: calling CreateClient() as a drop-in replacement, using named clients for a particular third-party API, and using typed clients to encapsulate calls to a third-party API.
Why it matters: These patterns let you choose an appropriate level of reuse, configuration, and encapsulation for remote API calls. - HttpClient internals preview: State that the section will first examine how HttpClient works under the hood before explaining how IHttpClientFactory provides its benefits.
Why it matters: Understanding the underlying HttpClient behavior provides context for why factory-managed creation is useful.
Key terms: IHttpClientFactory, HttpClient, HttpClientHandler, middleware pipeline, CreateClient(), named clients, typed clients, third-party API
33.2.1 Using IHttpClientFactory to manage HttpClientHandler lifetime
This section explains how IHttpClientFactory separates HttpClient from its handler pipeline, pooling and rotating handlers to avoid socket exhaustion and respect DNS changes.
- Outbound handler pipeline: Describe the HttpClient outbound handler pipeline, including the final HttpClientHandler that makes the actual remote connection. Explain that handlers can modify an outgoing request and inspect the incoming response.
Why it matters: The handler pipeline is where shared outbound behaviors can be applied around every HTTP call. - Handler pooling and rotation: Explain how IHttpClientFactory separates HttpClient lifetime from HttpClientHandler lifetime by pooling handlers and rotating the active handler every two minutes. Relate handler reuse to avoiding socket exhaustion, and handler rotation plus eventual disposal to making new connections that respect DNS changes.
Why it matters: This lifetime management lets an application make remote calls without either rapidly exhausting sockets or indefinitely using stale DNS information. - Factory registration and client creation: Describe how to add IHttpClientFactory to application services and inject the singleton factory into another service, then call CreateClient() to obtain an HttpClient backed by a pooled handler. Explain why this is a low-change refactoring pattern: normally only HttpClient creation code changes.
Why it matters: This gives an existing ASP.NET Core application a practical path to safer, factory-managed HTTP calls. - SocketsHttpHandler tradeoff: Distinguish the modern SocketsHttpHandler from the older HttpClientHandler lifetime issue, and justify why IHttpClientFactory is still generally recommended in DI-based applications. State that SocketsHttpHandler can be manually configured for connection pooling and recycling when IHttpClientFactory cannot be used.
Why it matters: Choosing the factory is not only about the legacy handler problem; it also provides DI integration and client-configuration features.
Key terms: HttpClient, HttpMessageHandler, HttpClientHandler, handler pipeline, outbound pipeline, IHttpClientFactory, socket exhaustion, TIME_WAIT, DNS rotation, handler pooling
33.2.2 Configuring named clients at registration time
This section shows how named IHttpClientFactory clients centralize reusable HttpClient configuration and are created by their case-sensitive names.
- Named client configuration: Explain how the Named Client pattern registers a string-named HttpClient with IHttpClientFactory and runs its configuration function whenever that named client is requested. Describe how this puts shared settings such as BaseAddress and default request headers in one central registration location.
Why it matters: Central configuration prevents every part of an application that calls the same API from having to repeat HttpClient setup. - Named client creation rules: Describe how consuming code requests the configured "rates" client from IHttpClientFactory by calling CreateClient with its exact name. Distinguish this from calling CreateClient with no name or with an unregistered name, which returns an unconfigured client.
Why it matters: Using the exact registered name is what ensures consuming code receives the shared configuration it expects.
Key terms: Named Client pattern, IHttpClientFactory, HttpClient, BaseAddress, default headers, configuration function, CreateClient, rates, unconfigured client, case-sensitive
33.2.3 Using typed clients to encapsulate HTTP calls
This section shows how typed clients use IHttpClientFactory and dependency injection to encapsulate calls and configuration for a remote HTTP API.
- Typed client encapsulation: Explain how a typed client accepts an HttpClient in its constructor and exposes application-specific methods that hide the remote API's URLs, HTTP verbs, and response types from its consumers.
Why it matters: A typed client keeps all remote-service communication details in one reusable place instead of spreading them through the application. - Typed client factory registration: Describe how registering AddHttpClient<ExchangeRatesClient> makes IHttpClientFactory create a configured pooled HttpClient and inject it when DI creates a new transient ExchangeRatesClient.
Why it matters: This registration combines HttpClient configuration, handler pooling, and service creation without requiring consuming code to manage any of them. - Typed client design options: Distinguish the typed-client alternatives of registering an interface for testable consumers and placing HttpClient configuration either in the AddHttpClient registration lambda or in the typed client's constructor.
Why it matters: These options let you choose where API-client details live and make code that consumes the client easier to test.
Key terms: typed client, HttpClient, ExchangeRatesClient, GetLatestRates(), AddHttpClient<ExchangeRatesClient>, IHttpClientFactory, transient service, pooled HttpClient, DI, ConfigureServices
33.3 Handling transient HTTP errors with Polly
This section shows how to add Polly-based transient-error policies to IHttpClientFactory clients so failed remote HTTP calls can be retried through the handler pipeline.
- Transient HTTP error handling: Explain what a transient HTTP error is and why retrying a request can sometimes succeed. Describe how temporary network failures or a temporarily unavailable remote API differ from a permanent failure.
Why it matters: Remote services and networks can fail temporarily, so clients need a deliberate way to respond to short-lived failures. - Polly handler integration: Describe how Microsoft.Extensions.Http.Polly integrates Polly into an IHttpClientFactory-created named or typed client as an HttpMessageHandler. State that the handler applies its policy automatically to requests made by that configured HttpClient.
Why it matters: Putting resilience behavior in the handler pipeline avoids duplicating retry code around every individual HTTP call. - Retry policy behavior: Describe the sample retry policy: it retries transient failures three times with an increasing delay between attempts, then returns the final failure to the client if the third retry fails. State the default failures it handles: HttpRequestException, HTTP 5xx responses, and HTTP 408 responses.
Why it matters: Knowing exactly which outcomes are retried and what happens after retries are exhausted makes client failure behavior predictable. - Retry strategy trade-offs: Justify choosing a retry policy based on the characteristics of the request and remote service. Explain that retrying is not always appropriate because a request may never succeed or may be better failed quickly, and Polly offers alternatives such as exponential backoff, circuit breaking, and bulkhead isolation.
Why it matters: A resilience policy can reduce temporary failures, but an unsuitable retry policy can waste time and add pressure to an unhealthy service.
Key terms: transient errors, remote API, IHttpClientFactory, HttpClient handler pipeline, Microsoft.Extensions.Http.Polly, Polly, HttpMessageHandler, named client, typed client, handler pipeline
33.4 Creating a custom HttpMessageHandler
This section shows how to create, register, and attach a custom DelegatingHandler so every request from a named or typed HttpClient can be modified automatically.
- Custom handler pipeline: Explain how a custom HttpMessageHandler participates in an HttpClient handler pipeline. Describe how it can modify an outgoing request, pass it to the next handler, and later inspect or modify the incoming response.
Why it matters: A handler centralizes cross-cutting HTTP behavior instead of requiring every API call to repeat it. - Handler implementation and registration: Describe the steps for adding a custom handler to a named or typed client: derive from DelegatingHandler, override SendAsync and call base.SendAsync, register the handler in dependency injection, and attach it with AddHttpMessageHandler<T>(). Explain how DI lifetime and registration order affect the handler.
Why it matters: Correct registration ensures the behavior runs for the intended clients in the intended order without inappropriate shared state. - Centralized API-key behavior: Explain why an API-key header is a suitable example of a custom handler: it automatically adds the required authentication header to every outgoing request for a configured third-party API instead of relying on each call site to remember it. State that the same handler can be attached to multiple named or typed clients, and can be added more than once to a pipeline.
Why it matters: Centralizing repeated API requirements makes calls more consistent and less error-prone.
Key terms: HttpMessageHandler, DelegatingHandler, HttpClientHandler, HttpRequestMessage, handler pipeline, SendAsync, base.SendAsync, AddHttpMessageHandler<T>, IHttpClientBuilder, singleton
33.5 Summary
This summary recaps how IHttpClientFactory safely creates and configures HttpClient instances, adds resilience policies, and extends calls with custom handlers.
Where this is used later
- Creating a worker service from a template (chapter 34) · IHttpClientFactory is registered and used for HTTP clients.
- Replacing dependencies in WebApplicationFactory (chapter 36) · IHttpClientFactory-based services can encapsulate outbound HTTP calls.
- Running background tasks on a timer (chapter 34) · IHttpClientFactory manages HttpClientHandler lifetime and rotation.
Concepts explained in this chapter
- IHttpClientFactoryIHttpClientFactory is the .NET dependency-injection service for creating configured HttpClient instances and managing their underlying handlers.
- pipelineA handler pipeline is an ordered chain of HTTP message handlers that can modify requests and inspect responses around a final transport handler.
- socket exhaustionSocket exhaustion is a condition in which excessive undisposed or unavailable network sockets prevent an application from establishing further connections.
- HTTP clientHttpClient is the .NET class for sending HTTP requests and receiving HTTP responses.
- typed clientA typed client is an application-specific wrapper around HttpClient that exposes remote-service operations through a strongly typed class.
- DNS problemA DNS rotation problem occurs when a long-lived client connection fails to recognize that a hostname now resolves to a different service address.
- named clientA named client is an IHttpClientFactory configuration identified by a string name and used to create consistently configured HttpClient instances.
- HttpMessageHandlerHttpMessageHandler is the .NET base abstraction for processing HTTP requests and responses in an HttpClient handler chain.
- PollyPolly is a .NET resilience library for defining policies that handle faults and control retries, timeouts, circuit breakers, and related behaviors.
- retryA retry policy is a resilience rule that repeats a failed operation according to configured retry conditions and limits.
- transient failureA transient error is a temporary failure, such as a network or service interruption, that may succeed when the operation is retried.
- HttpClientHandlerHttpClientHandler is the .NET HttpMessageHandler that establishes network connections and sends HTTP requests for HttpClient.
- RESTA RESTful API is a web API designed around REST constraints and resource-oriented HTTP interactions.
- web serviceAn HTTP API application exposes application functionality through endpoints that communicate using the HTTP protocol.
- Microsoft.Extensions.Http.PollyMicrosoft.Extensions.Http.Polly is a .NET integration library that applies Polly resilience policies to HttpClient instances created by IHttpClientFactory.
- AddHttpClientAddHttpClient is a .NET dependency-injection extension method that registers an HttpClient and optionally associates its configuration with a typed or named client.
- AddHttpMessageHandlerAddHttpMessageHandler is an IHttpClientBuilder extension method that adds a delegating handler type to an HttpClient handler pipeline.
- DelegatingHandlerDelegatingHandler is a .NET HttpMessageHandler base class that forwards processing to an inner handler while allowing additional behavior.
- SendAsyncSendAsync is the asynchronous HttpMessageHandler method that processes an HTTP request and returns its response.
- custom HttpMessageHandlerA custom HttpMessageHandler is an application-defined HTTP pipeline component that modifies or observes requests and responses.
- SocketsHttpHandlerSocketsHttpHandler is the .NET HTTP message handler that implements HTTP networking with managed sockets and supports connection pooling and recycling.
- connection poolingConnection pooling is the reuse of established network connections across multiple client requests to reduce connection-setup overhead.
- handlerA handler is an application component that receives a framework-dispatched request or event and performs the corresponding processing.
- API keyAn API key is a secret credential used to authenticate an application or request to a remote service.
- domain name systemDNS is the hierarchical naming system that resolves hostnames to IP addresses and other network records.
These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.
