Partilhar via


HttpClientFactory usa SocketsHttpHandler como manipulador primário

HttpClientFactory Permite configurar um HttpMessageHandler pipeline para objetos nomeados e digitados HttpClient . O manipulador mais interno, ou aquele que realmente envia a solicitação no fio, é chamado de manipulador primário. Se não estiver configurado, esse manipulador anteriormente sempre foi um HttpClientHandlerarquivo . Embora o manipulador primário padrão seja um detalhe de implementação, havia usuários que dependiam dele. Por exemplo, alguns usuários convertem o manipulador primário para HttpClientHandler definir propriedades como ClientCertificates, UseCookiese UseProxy.

Com essa alteração, o manipulador primário padrão é um SocketsHttpHandler em plataformas que o suportam. Em outras plataformas, por exemplo, o .NET Framework HttpClientHandler continua a ser usado.

SocketsHttpHandler Agora também tem a PooledConnectionLifetime propriedade predefinida para corresponder ao HandlerLifetime valor. (Ele reflete o valor mais recente, se HandlerLifetime foi configurado pelo usuário).

Versão introduzida

.NET 9 Visualização 6

Comportamento anterior

O manipulador primário padrão era HttpClientHandler. Transmiti-lo para HttpClientHandler atualizar as propriedades funcionou.

services.AddHttpClient("test")
    .ConfigurePrimaryHttpMessageHandler((h, _) =>
    {
        ((HttpClientHandler)h).UseCookies = false;
    });

// This worked.
var client = httpClientFactory.CreateClient("test");

Novo comportamento

Em plataformas onde SocketsHttpHandler é suportado, o manipulador primário padrão agora SocketsHttpHandler está com PooledConnectionLifetime definido para o HandlerLifetime valor. Convertê-lo para HttpClientHandler atualizar as propriedades lança um InvalidCastExceptionarquivo .

Por exemplo, o mesmo código da seção Comportamento anterior agora lança um InvalidCastException:

System.InvalidCastException: Não é possível converter objeto do tipo 'System.Net.Http.SocketsHttpHandler' para digitar 'System.Net.Http.HttpClientHandler'.

Tipo de mudança de rutura

Esta mudança é uma mudança comportamental.

Razão para a alteração

Um dos problemas HttpClientFactory mais comuns que os usuários enfrentam é quando um Named ou Typed cliente erroneamente é capturado em um serviço singleton ou, em geral, armazenado em algum lugar por um período de tempo maior do que o especificado HandlerLifetime. Como HttpClientFactory não é possível girar esses manipuladores, eles podem acabar não respeitando as alterações de DNS.

Esse problema pode ser atenuado usando SocketsHttpHandler, que tem uma opção para controlar PooledConnectionLifetime. Da mesma forma que o HandlerLifetime, o tempo de vida da conexão em pool permite recriar regularmente conexões para captar alterações de DNS, mas em um nível mais baixo. Um cliente com PooledConnectionLifetime configuração pode ser usado com segurança como um singleton.

É, infelizmente, fácil e aparentemente "intuitivo" injetar um Typed cliente em um singleton. Mas é difícil ter qualquer tipo de verificação ou analisador para se certificar de que HttpClient não é capturado quando não deveria ser capturado. Também é difícil solucionar os problemas resultantes. Portanto, como medida preventiva — para minimizar o impacto potencial de padrões de uso errôneos — a SocketsHttpHandler mitigação agora é aplicada por padrão.

Essa alteração afeta apenas os casos em que o cliente não foi configurado pelo usuário final para usar um personalizado PrimaryHandler (por exemplo, via ConfigurePrimaryHttpMessageHandler<THandler>(IHttpClientBuilder)).

Há três opções para contornar a mudança de rutura:

  • Especifique e configure explicitamente um manipulador primário para cada um dos seus clientes:

    services.AddHttpClient("test")
      .ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler() { UseCookies = false });
    
  • Substitua o manipulador primário padrão para todos os clientes usando ConfigureHttpClientDefaults(IServiceCollection, Action<IHttpClientBuilder>):

    services.ConfigureHttpClientDefaults(b =>
      b.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler() { UseCookies = false }));
    
  • Na ação de configuração, verifique se há ambos HttpClientHandler e SocketsHttpHandler:

    services.AddHttpClient("test")
      .ConfigurePrimaryHttpMessageHandler((h, _) =>
      {
          if (h is HttpClientHandler hch)
          {
              hch.UseCookies = false;
          }
    
          if (h is SocketsHttpHandler shh)
          {
              shh.UseCookies = false;
          }
      });
    

APIs afetadas