如何在不使用HttpClientFactory的情况下向HttpClient添加多个HttpMessageHandler

28
我有一个控制台应用程序,使用HttpClient进行网络请求。
var client = new HttpClient();

我正在尝试向其中添加多个HttpMessageHandler(实际上是自定义实现的DelegatingHandler),但是HttpClient的构造函数只接受单个HttpMessageHandler

class LoggingHandler : DelegatingHandler { //... }
class ResponseContentProcessingHandler : DelegatingHandler { //... }

这是可以的...

var client = new HttpClient(new LoggingHandler()); // OK

但是这个无法编译:
var client = new HttpClient(
                new LoggingHandler(), 
                new ResponseContentProcessingHandler()); // Sadness

因为我正在使用.NET 4.0,所以无法使用HttpClientFactory,这通常是解决此问题的方法。
HttpClient client = HttpClientFactory.Create(
                       new LoggingHandler(),
                       new ResponseContentProcessingHandler());

因为我只是在控制台应用程序中,而不是在ASP.NET应用程序中,所以我也无法做到这一点:
GlobalConfiguration.Configuration
                   .MessageHandlers
                   .Add(new LoggingHandler()
                   .Add(new ResponseContentProcessingHandler());

我已经查看了HttpClientFactory的源代码,似乎没有任何东西无法在.NET 4.0中编译,但是除非自己编写一个(受Microsoft源代码启发的)工厂,否则是否有一种手动添加多个HTTP消息处理程序到HttpClient的方法?

无论您使用控制台还是asp.net,Web API都旨在使用http协议。 - Zara_me
@Zara_me 你是在说你可以在控制台应用程序中访问 GlobalConfiguration.Configuration.MessageHandlers 吗?因为我说我做不到。HTTP协议与此有什么关系? - Jeff
如果您无法在控制台中使用 HttpClientFactory,那么使用最小的网页托管并发送 Httpconfiguration 对象是否更好,并且在测试时使用 Fiddler2。 - Zara_me
5个回答

35

DelegatingHandler有一个保护的构造函数,接受内部处理程序的处理程序。如果您可以控制所有自定义处理程序,我认为您可以添加一个公共构造函数,调用保护的构造函数,例如:

public class CustomHandler : DelegatingHandler
{
    public CustomHandler(HttpMessageHandler innerHandler) : base(innerHandler)
    {
    }
}

并将它们连接在一起:

var client = new HttpClient(
    new CustomHandler(
        new OtherCustomerHandler(
            new HttpClientHandler()
        )
    )
);

3
感谢您展示如何链式调用多个处理程序,特别是使用HttpClientHandler作为最内层处理程序以保留使用HttpClient无参数构造函数时的行为。文档中提到了链接,但我找不到任何示例!+1 - Eric Mutta

22

我认为你可以像这样做:

var loggingHandler = new LoggingHandler();
var responseContentProcessingHandler =  new ResponseContentProcessingHandler();
loggingHandler.InnerHandler = responseContentProcessingHandler;
var client = new HttpClient(loggingHandler);

使用DelegatingHandler,您无需为链接目的创建自定义处理程序。这确实是DelegatingHandler的目的。


4

如果使用AddHttpClient扩展方法,您可以使用IHttpClientBuilder来配置所有的DelegateHandler链。

ServiceCollection
    .AddHttpClient("MyClient")
    .ConfigureHttpMessageHandlerBuilder(builder =>
    {
        builder.AdditionalHandlers.Add(builder.Services.GetRequiredService<YourFirstHandler>());
        builder.PrimaryHandler = builder.Services.GetRequiredService<YourLastHandler>();
    });
} 

1
在 .Net 4.0 平台上使用控制台应用程序实现最佳解决方案是将 httpConfiguration 传递给您的 WebAPI 库,如果您没有 WebAPI 代码,则只需在 webhost asp.net 应用程序的 global.ascx 文件中编写此代码。
     protected void Application_Start(object sender, EventArgs e)
             {
        var config = GlobalConfiguration.Configuration;
        WebAPIConfig.Configure(config);


    }

   //Code that will configure all message handlers in webapi    
          public static void Configure(HttpConfiguration configure)
   {


        configure.MessageHandlers.Add(new xyzhandler());
       configure.MessageHandlers.Add(new ABCHandler());

   }

在您的控制台应用程序中,放置托管Web API的Web主机的URI。
   private const string APiUri="http://localhost/api/get"
   using(HttpClient cleint=new HttpClient()){
     var response await cleint.GetAsync(ApiUri);
    ....
               }

实现“调用/注册”多个处理程序是我无法理解的 - 你能编辑这个答案并包含一个代码片段来解释你的意思吗? - Jeff

0

对于.NET C#,如果有人也想做类似的事情,我能够通过静态构建器/工厂来实现它:

namespace SomeNamespace {
  public class ConcreteDelegatingHandler : DelegatingHandler {}

  public class DelegateHandlerFactory
  {
    public static DelegatingHandler Build(dynamic[] handlers)
    {
      DelegatingHandler wrapper = new ConcreteDelegatingHandler();
      DelegatingHandler current = wrapper;
      HttpMessageHandler last = handlers.Last();

      foreach (dynamic handler in handlers)
      {
        if (handler == last)
          current.InnerHandler = handler;
        else if (current is DelegatingHandler)
        {
          current.InnerHandler = handler;
          current = handler;
        }
        else throw new InvalidOperationException("Intermediate handlers must be of type DelegatingHandler");
      }  
      return wrapper;
    }
  }
}

使用方法:

HttpMessageHandler[] handlers = {
  myDelegatingHandler1,
  myDelegatingHandler2,
  ...
  myDelegatingHandlerN
};

var handler = DelegateHandlerFactory.Build(handlers);
HttpClient Client = new HttpClient(handler, true);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接