如何使用 autofac 注册类型化的 httpClient 服务?
Posted
技术标签:
【中文标题】如何使用 autofac 注册类型化的 httpClient 服务?【英文标题】:How to register typed httpClient service with autofac? 【发布时间】:2019-11-10 17:34:39 【问题描述】:我正在创建 MVC Web 应用程序,该应用程序使用 .net core 2.2 调用 api,使用单独的 HttpClient
s 来调用每个控制器(相同的 api)。
例如:
对于用户控制器操作:UserService (httpclient) 对于后期控制器操作:PostService (httpclient)在 startup.cs
我使用 DI 作为:
services.AddHttpClient<IUserService, UserService>();
services.AddHttpClient<IPostService, PostService>();
在我的处理程序中:
public class CommandHandler : IRequestHandler<Command, BaseResponse>
private readonly IUserService _userService;
public CommandHandler(IUserService userService)
_userService = userService;
public Task<BaseResponse> Handle(Command request, CancellationToken cancellationToken)
throw new System.NotImplementedException();
但是在调用命令处理程序时出现此错误:
没有找到任何构造函数 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' 类型 'xxx.Application.Services.Users.UserService' 可以被调用 可用的服务和参数:无法解析参数 构造函数'Void'的'System.Net.Http.HttpClient httpClient' .ctor(System.Net.Http.HttpClient, xxx.Application.Configurations.IApplicationConfigurations, Microsoft.Extensions.Logging.ILogger`1[xxx.Application.Services.Users.UserService])'。
但我已经在 autofac 模块中注册了服务:
public class ServiceModule : Module
protected override void Load(ContainerBuilder builder)
builder.RegisterAssemblyTypes(typeof(ServiceModule).Assembly)
.Where(t => t.Namespace.StartsWith("xxx.Application.Services"))
.AsImplementedInterfaces().InstancePerLifetimeScope();
这是我的UserService
类构造函数:
public UserService (HttpClient httpClient, IApplicationConfigurations applicationConfig, ILogger<UserService> logger)
_httpClient = httpClient;
_applicationConfig = applicationConfig;
_logger = logger;
_remoteServiceBaseUrl = $"_applicationConfig.WebApiBaseUrl";
我有两个问题:
-
以上错误是什么意思?
为 api 中的不同控制器使用单独的 httpclients 是一种好习惯吗?
【问题讨论】:
我认为你误解了这个错误。 Autofac 无法实例化UserService
,因为构造函数参数之一是您尚未注册的HttpClient
。该错误与CommandHandler
无关。您应该包含 UserService
的构造函数代码。
谢谢。我已将其包含在问题中(已更新)
这能回答你的问题吗? Autofac Exception: Cannot resolve parameter of constructor 'Void .ctor
注册顺序很重要。见:github.com/autofac/Autofac/issues/934
您能分享更多来自您的 startup.cs 文件的代码吗?
【参考方案1】:
通过做
services.AddHttpClient<IUserService, UserService>();
当请求IUserService
时,您将配置本机.net 核心依赖注入以将HttpClient
注入UserService
。
那你就做吧
builder.RegisterAssemblyTypes(typeof(ServiceModule).Assembly)
.Where(t => t.Namespace.StartsWith("xxx.Application.Services"))
.AsImplementedInterfaces().InstancePerLifetimeScope();
这将删除IUserService
的本机依赖注入配置。 IUserService
现在注册到 UserService
没有任何 HttpClient
。
添加HttpClient
的最简单方法是像这样注册它:
builder.Register(c => new HttpClient())
.As<HttpClient>();
或
services.AddHttpClient(); // register the .net core IHttpClientFactory
builder.Register(c => c.Resolve<IHttpClientFactory>().CreateClient())
.As<HttpClient>();
如果你想为一个特定的服务配置你的 httpclient,你可以创建一个 autofac 模块来添加这样的参数:
public class HttpClientModule<TService> : Module
public HttpClientModule(Action<HttpClient> clientConfigurator)
this._clientConfigurator = clientConfigurator;
private readonly Action<HttpClient> _clientConfigurator;
protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration)
base.AttachToComponentRegistration(componentRegistry, registration);
if (registration.Activator.LimitType == typeof(TService))
registration.Preparing += (sender, e) =>
e.Parameters = e.Parameters.Union(
new[]
new ResolvedParameter(
(p, i) => p.ParameterType == typeof(HttpClient),
(p, i) =>
HttpClient client = i.Resolve<IHttpClientFactory>().CreateClient();
this._clientConfigurator(client);
return client;
)
);
;
然后
builder.RegisterModule(new HttpClientModule<UserService>(client =>
client.BaseAddress = new Uri("https://api.XXX.com/");
client.DefaultRequestHeaders.Add("Accept", "application/vnd.XXX.v3+json");
client.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-XXX");
));
【讨论】:
很棒的代码,但与 Autofac 6 不兼容。【参考方案2】:Cyril 使用 Autofac 模块的实现非常出色,但不幸的是与 Autofac 6.0+ 不兼容。
为了在 Autofac 6.0+ 中为特定服务配置 HttpClient
,需要实现 Autofac 中间件:
public class HttpClientMiddleware<TService> : IResolveMiddleware
private readonly Action<HttpClient> _clientConfigurator;
public HttpClientMiddleware(Action<HttpClient> clientConfigurator)
_clientConfigurator = clientConfigurator;
public PipelinePhase Phase => PipelinePhase.ParameterSelection;
public void Execute(ResolveRequestContext context, Action<ResolveRequestContext> next)
if (context.Registration.Activator.LimitType == typeof(TService))
context.ChangeParameters(context.Parameters.Union(
new[]
new ResolvedParameter(
(p, _) => p.ParameterType == typeof(HttpClient),
(_, i) =>
var client = i.Resolve<IHttpClientFactory>().CreateClient();
_clientConfigurator(client);
return client;
)
));
next(context);
然后可以使用中间件注册服务:
builder.RegisterType<UserService>()
.As<IUserService>()
.ConfigurePipeline(p =>
p.Use(new HttpClientMiddleware<UserService>(client =>
client.BaseAddress = new Uri("https://api.XXX.com/");
client.DefaultRequestHeaders.Add("Accept", "application/vnd.XXX.v3+json");
client.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-XXX");
));
);
【讨论】:
以上是关于如何使用 autofac 注册类型化的 httpClient 服务?的主要内容,如果未能解决你的问题,请参考以下文章