C# 如何在不调用Configure()的情况下全局设置Flurl调用上的HttpMessageHandler
我正在构建一个小型库供内部使用,它依赖Flurl来处理所有传出的HTTP调用 我想让这个库的用户能够选择使用我们的HTTP调用跟踪基础结构,如下所示: Startup.cs:C# 如何在不调用Configure()的情况下全局设置Flurl调用上的HttpMessageHandler,c#,asp.net-core,flurl,C#,Asp.net Core,Flurl,我正在构建一个小型库供内部使用,它依赖Flurl来处理所有传出的HTTP调用 我想让这个库的用户能够选择使用我们的HTTP调用跟踪基础结构,如下所示: Startup.cs: ... public void ConfigureServices(IServiceCollection services) { .... services.AddTracing(options => ...); .... } ... ... public void Configu
...
public void ConfigureServices(IServiceCollection services)
{
....
services.AddTracing(options => ...);
....
}
...
...
public void ConfigureServices(IServiceCollection services)
{
....
// Configure() is being called inside AddTracing()
services.AddTracing(options => ...);
....
// This is a second call to Configure()
FlurlHttp.Configure(settings => {
var jsonSettings = new JsonSerializerSettings
{
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor
};
settings.JsonSerializer = new NewtonsoftJsonSerializer(jsonSettings);
});
}
...
我当前的AddTracing()
实现如下所示:
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddTracing(this IServiceCollection services, Action<TracingOptions> configureOptions)
{
var tracingOptions = new TracingOptions();
configureOptions(tracingOptions);
// Make these options available in DI container
services.AddSingleton(tracingOptions);
FlurlHttp.Configure(settings =>
{
settings.HttpClientFactory = new TracingHttpClientFactory(tracingOptions.ApplicationName);
});
return services;
}
}
public class TracingHttpClientFactory : DefaultHttpClientFactory
{
private readonly string _applicationName;
public TracingHttpClientFactory(string applicationName)
{
_applicationName = applicationName;
}
// override to customize how HttpMessageHandler is created/configured
public override HttpMessageHandler CreateMessageHandler()
{
var tracingHandler = new TracingHandler(_applicationName, base.CreateMessageHandler());
return tracingHandler;
}
}
这是可行的,但我面临的问题是Configure()
的文档内容是:在应用程序启动时只应调用一次
因此,通过添加跟踪(可选),我“浪费”了对
Configure()
的调用。在使用跟踪的场景中,我还需要在之后调用Configure()
我可能需要在Startup.cs中调用configure的示例如下:
...
public void ConfigureServices(IServiceCollection services)
{
....
services.AddTracing(options => ...);
....
}
...
...
public void ConfigureServices(IServiceCollection services)
{
....
// Configure() is being called inside AddTracing()
services.AddTracing(options => ...);
....
// This is a second call to Configure()
FlurlHttp.Configure(settings => {
var jsonSettings = new JsonSerializerSettings
{
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor
};
settings.JsonSerializer = new NewtonsoftJsonSerializer(jsonSettings);
});
}
...
要点是,AddTracing()
的每个使用者都应该能够根据自己的需要配置Flurl。AddTracing()
的要点是简单地用一些额外的可选功能“增压”Flurl。它不是要接管Configure()
——而是要扩展它
我一直在阅读文档,尽管有许多地方可以进行配置,但我无法找到一种方法,在不调用Configure()
的情况下,将我的TracingHandler
(这是一个HttpMessageHander
)放入每个请求中
对于我描述的场景,是否有合适的实现?之所以提出“启动时调用一次”建议,是因为它涉及全局范围。如果您开始在许多不同的地方(可能在不同的线程上,例如在控制器操作方法中)处理全局设置,您可能最终会遇到奇怪的竞争条件和其他不可预测的行为
在您的情况下,是的,您调用了两次
Configure
,但调用是连续的、无冲突的,并且在ASP.NET核心应用程序中的“启动”代码所在的位置正确完成。最重要的是,他们在用Flurl打电话之前就完成了。所以您在这里所做的很好。“在使用跟踪的场景中,我还需要在之后调用Configure()。”为什么?你能详细说明这一部分吗?我做了一个编辑,补充了一些说明。这很有帮助,谢谢。这对我现在来说可能是可行的,但我马上想到一些库消费者在不知道他们做错了什么的情况下意外地覆盖了我的HttpClientFactory。一般来说,跟踪库使用者不应该知道或关心“services.AddTracing()”在做什么。我知道你是Flurl的主要贡献者,所以我确信如果有更好的方法,你会让我知道:)再次感谢你的帮助。请记住,services.AddXXX
方法通常用于添加/配置注入服务,而不是更改全局设置。“更好的方法”是将工厂设置为injectedFlurlClient
s,这更符合IoC模式,但您必须告诉用户“当您像这样使用injected client时跟踪工作”,而不是“无论您如何使用Flurl,跟踪在任何地方都工作”。一句话:如果你的库可以在全球范围内配置Flurl,那么你的库的消费者也可以,除了告诉你的用户“不要那样做”之外,我想不出其他解决方法。是的,这是有道理的。我意识到我通过这种模式滥用了AddXXX。这是我写这篇文章的首要原因。感谢您的反馈。