IHttpClientFactory in ASP.NET Core Typed Clients

Jos*_*osh 1 c# dependency-injection dotnet-httpclient asp.net-core asp.net-core-2.1

如本文所述,当我尝试对HttpClient使用类型化的客户端时收到错误消息:https ://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/http-requests?view = aspnetcore-2.2

这是我的设置:

Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    services.AddDefaultAWSOptions(Configuration.GetAWSOptions());

    services.AddHttpClient<IPStackService>(c =>
    {
        c.BaseAddress = new Uri(Configuration["IPStackURL"]);
    });

    services.AddHttpContextAccessor();

    services.AddTransient<IIPStackService, IPStackService>();
}
Run Code Online (Sandbox Code Playgroud)

控制器:

[Route("api/[controller]")]
    [ApiController]
    public class RenderController : ControllerBase
    {
        private IConfiguration _configuration;
        private readonly ILogger _logger;
        private IIPStackService _ipMetaDataService;

        public RenderController(IConfiguration configuration,
                                ILogger<RenderController> logger,
                                IIPStackService ipStackService)
        {
            _configuration = configuration;
            _ipStackService = ipStackService;
            _logger = logger;
        }

        [HttpGet]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [Produces("text/plain")]
        public async Task<ActionResult> GetAsync(string hostId, string clientUtc)
        {
            var ip = Common.ResolveIPAddress(HttpContext);

            var ipMetaData = await _ipStackService.GetByIPAsync(ip, _configuration["AccessKey"]);

            // removed for clarity

            return allowed ? Ok(true) : Ok(null);
        }
    }
Run Code Online (Sandbox Code Playgroud)

服务:

public class IPStackService : IIPStackService
{
    private readonly ILogger _logger;
    private readonly HttpClient _httpClient;

    public IPStackService(ILogger<IPStackService> logger, HttpClient client)
    {
        _logger = logger;
        _httpClient = client;
    }

    public async Task<IPMetaDataModel> GetByIPAsync(string ip, string accessKey)
    {
        var ipMetaData = new IPMetaDataModel();

        var response = await _httpClient.GetAsync($"/{ip}?access_key={accessKey}");
        response.EnsureSuccessStatusCode();

        // removed for clarity

        return ipMetaData;
    }
}
Run Code Online (Sandbox Code Playgroud)

收到错误:

处理请求时发生未处理的异常。InvalidOperationException:尝试激活“ WebScreen.Gate.Services.IPStackService”时,无法解析类型为“ System.Net.Http.HttpClient”的服务。Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(Type serviceType,Type ImplementationType,CallSiteChain callSiteChain,ParameterInfo [] parameters,bool throwIfCallSiteNotFound)

如何为HttpClient正确设置类型化的客户端?

Ham*_*eed 7

IPStackService(代码反射)的Activator HttpClient在DI集合中找不到,这就是为什么出现异常的原因(特别是services.AddTransient<IIPStackService, IPStackService>())。

顺便说一句,您IPStackService在DI集合中两次添加了服务:

  1. services.AddHttpClient<IPStackService>(...);
  2. services.AddTransient<IIPStackService, IPStackService>();

您只需要一个,并且由于您正在使用类型化的客户端,因此可以通过删除第二个并保留第一个来解决您的问题,并通过将契约添加到实现中来稍微改变第一个,如下所示:

services.AddHttpClient<IIPStackService, IPStackService>(c =>
{
     c.BaseAddress = new Uri(Configuration["IPStackURL"]);
});
Run Code Online (Sandbox Code Playgroud)

AddHttpClient将结合照顾HttpClient到您的服务。


如果要使用services.AddTransient<IIPStackService, IPStackService>();而不是services.AddHttpClient,则需要将添加HttpClient为:

services.AddTransient<HttpClient>();
Run Code Online (Sandbox Code Playgroud)

  • 恕我直言,我认为使用`services.AddTransient &lt;HttpClient&gt;();`添加HttpClient不是一个好主意。这将为每个请求创建HttpClient,如果未正确处理HttpClient实例,则可能导致打开套接字。而AddHttpClient使用IHttpClientFactory并在后台管理HttpMessageHandler的生存期。 (2认同)