首页 > 解决方案 > 类型化的 HttpClient 仅适用于 WebApplicationFactory 的 CreateClient()

问题描述

我为默认的 WeatherForecast API 项目创建了一个客户端,我想在集成测试中使用它:

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;

namespace API_ClientTest
{

    public class WeatherClient : IWeatherClient
    {
        public HttpClient _client { get; }

        public WeatherClient(HttpClient client)
        {
            client.BaseAddress = new Uri("http://localhost:80/");
            client.DefaultRequestHeaders.Add("Accept",
            "application/json");
            _client = client;
        }

        public async Task<string> GetWeatherForecast()
        {
            var response = await _client.GetAsync("weatherforecast");
            response.EnsureSuccessStatusCode();
            return await response.Content.ReadAsStringAsync();
        }
    }

}


    using System.Threading.Tasks;

    namespace API_ClientTest
    {
        public interface IWeatherClient
        {
            Task<string> GetWeatherForecast();
        }
    }

对于我的集成测试,我使用 WebApplicationFactory:

using System.Linq;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace API_ClientTest
{
    public class WeatherServiceFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
    {
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.ConfigureServices(services =>
            {
                // Build the service provider.
                var sp = services.BuildServiceProvider();

                //Typed clients (because types are cool)
                services.AddHttpClient("WeatherClient")
                .AddTypedClient<WeatherClient>()
                .AddTypedClient<IWeatherClient, WeatherClient>();
            });
        }
    }
}
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Xunit;

namespace API_ClientTest
{
    public class WeatherServiceTest : IClassFixture<WeatherServiceFactory<API_ClientTest.Startup>>
    {
        private readonly WeatherServiceFactory<API_ClientTest.Startup>
           _factory;
        public WeatherServiceTest(WeatherServiceFactory<API_ClientTest.Startup> factory)
        {
            _factory = factory;
        }

        [Fact]
        public async Task Get_GenerateResponse()
        {
            // Arrange
            var weatherClient = _factory.Services.GetRequiredService<IWeatherClient>();
            var factoryClient = _factory.CreateClient();
            // Works
            var factoryResponse = await factoryClient.GetAsync("weatherforecast");
            // Does not work
            var response = await weatherClient.GetWeatherForecast();
        }
    }
}

所以工厂的客户端可以与 API 通信,但是依赖注入(我从工厂的服务中获取)的客户端会抛出 System.Net.Http.HttpRequestException。

Message: 
    System.Net.Http.HttpRequestException : No connection could be made because the target machine refused it. (Translated by author)
    ---- System.Net.Sockets.SocketException : Es konnte keine Verbindung hergestellt werden, da der Zielcomputer die Verbindung verweigerte.
  Stack Trace: 
    ConnectHelper.ConnectAsync(String host, Int32 port, CancellationToken cancellationToken)
    HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean allowHttp2, CancellationToken cancellationToken)
    HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    HttpConnectionPool.GetHttpConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
    RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    LoggingHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    LoggingScopeHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
    WeatherClient.GetWeatherForecast() line 24
    WeatherServiceTest.Get_GenerateResponse() line 26
    --- End of stack trace from previous location where exception was thrown ---
    ----- Inner Stack Trace -----
    ConnectHelper.ConnectAsync(String host, Int32 port, CancellationToken cancellationToken)

你能解释一下不同行为的原因吗?谢谢

编辑:来自工厂的 HttpClient 直接与 WebApi 通信,而来自 WeatherService 的 HttpClient 通过网络向 localhost 发送一个 http 请求。目标是在我的 WeatherClient 中重新创建第一个 HttpClient 的行为。

一个临时的解决方法是使用工厂的 HttpClient 实例初始化类型化的客户端:

var weatherClient = new WeatherClient (_factory.CreateClient());

标签: c#asp.net-core

解决方案


推荐阅读