34

I have a console application that uses HttpClient to make web requests.

var client = new HttpClient();

I'm trying to add multiple HttpMessageHandler to it (custom implementations of DelegatingHandler, really) but the constructor for HttpClient only takes a single HttpMessageHandler.

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

this is ok...

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

but this doesn't compile:

var client = new HttpClient(
                new LoggingHandler(), 
                new ResponseContentProcessingHandler()); // Sadness

Because I'm targeting .NET 4.0, I cannot use HttpClientFactory, which is how the solution to this problem is commonly explained:

HttpClient client = HttpClientFactory.Create(
                       new LoggingHandler(),
                       new ResponseContentProcessingHandler());

Because I'm just in a console application, rather than in an ASP.NET application, I can't do this either:

GlobalConfiguration.Configuration
                   .MessageHandlers
                   .Add(new LoggingHandler()
                   .Add(new ResponseContentProcessingHandler());

I've looked at the source for HttpClientFactory and there doesn't seem to be anything in there that wouldn't compile in .NET 4.0, but short of rolling my own factory ("inspired" by Microsoft's source code), is there a way to manually add many HTTP message handlers to the HttpClient?

3
  • It does not matter if you are using console or asp.net, Web API meant to be using http protocol anyway. Commented Mar 17, 2016 at 17:01
  • @Zara_me are you saying that you can get to GlobalConfiguration.Configuration.MessageHandlers in a console application? Because I'm saying I can't. What does the HTTP protocol have to do with it? Commented Mar 17, 2016 at 17:05
  • if you can not use HttpClientFactory in console , isn't better to use a minimal web page hosting and sending Httpconfiguration object and for testing use Fiddler2. Commented Mar 17, 2016 at 17:20

5 Answers 5

41

DelegatingHandler has a protected constructor that takes a handler for the inner handler. If you have control over all your custom handlers, I would think you can add a public constructor that calls the protected constructor, like:

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

and chain them thus:

var client = new HttpClient(
    new CustomHandler(
        new OtherCustomerHandler(
            new HttpClientHandler()
        )
    )
);
Sign up to request clarification or add additional context in comments.

1 Comment

+1 for actually showing how to chain multiple handlers, especially with HttpClientHandler appearing at the innermost level so it can preserve the behaviour you get from HttpClient when you use its parameterless constructor. The docs mention chaining but I couldn't see an example anywhere!
27

I think you can just do something like this:

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

So that you don't need to create a CustomHandler just for the chaining purpose. That's really the purpose of DelegatingHandler.

Comments

7

In case of using AddHttpClient extension method you can use IHttpClientBuilder to configure all chain of DelegateHandler-s.

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

Comments

1

To Achieve with Console application on .Net 4.0 platform,best possible solution is to pass httpConfiguration to your webapi library or if you dont hold WebAPI code just write this code in global.ascx file of webhost asp.net application

     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());

   }

In your Console Application,place uri of your webhost hosting webapi

   private const string APiUri="http://localhost/api/get"
   using(HttpClient cleint=new HttpClient()){
     var response await cleint.GetAsync(ApiUri);
    ....
               }

1 Comment

the implementation of "calling/registering" the multiple handlers is what's eluding me - can you edit this answer to include a code snippet explaining what you mean?
0

For .NET C#, in case someone who is also looking to do something similar, I was able to archive it with a static builder/factory:

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;
    }
  }
}

Usage:

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

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.