2

In asp.net - using Message Handlers - I can customize the request/response by adding message handlers.

So, a request comes in , going through multiple message handlers and then the response is back through the same handlers( in opposite direction).

enter image description here

So, for example : if I attach 2 message handlers : (yes I know, async/await is preferred, but that's from a book)

public class CustomMessageHandler1 : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        Debug.WriteLine("CustomMessageHandler1 request invoked");
        return base.SendAsync(request, cancellationToken)
                   .ContinueWith(task =>
                                      {
                                      Debug.WriteLine("CustomMessageHandler1 response invoked");
                                      var response = task.Result;
                                      return response;
                                      });
    }
}


public class CustomMessageHandler2 : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        Debug.WriteLine("CustomMessageHandler2 request invoked");
        return base.SendAsync(request, cancellationToken)
                   .ContinueWith(task => 
                                      {
                                      Debug.WriteLine("CustomMessageHandler2 response invoked");
                                      var response = task.Result;
                                      return response;
                                 });
    }
}

Let's not forget to register those in global.asax :

var config = GlobalConfiguration.Configuration;
config.MessageHandlers.Add(new CustomMessageHandler1());
config.MessageHandlers.Add(new CustomMessageHandler2());

And the result is :

enter image description here

As you can see , like I said and like this article says : The execution is symmetrical.

Great.

But then I thought to myself - how did they do that symmetrical execution?

So I succeed creating my own demo of symmetrical execution using continuation:

void Main()
{
 Method1() ;    
}

  public async  Task  Method1 ()
  {
   Console.WriteLine ("Method_1"); //alias to request
   await Method2();
   Console.WriteLine ("Finished Method_1");  //alias to response
  }


 public async Task  Method2 ()
 {
  Console.WriteLine ("Method_2");  //alias to request
  await Task.FromResult("...");//dummy
  Console.WriteLine ("Finished Method_2");  //alias to response

 }

And the result was indeed symetrical :

Method_1
Method_2
Finished Method_2
Finished Method_1

But in my code Method1 called Method2 and that's why it worked !.

But in the first code above - they do NOT call each other ! it's like something is invoking only the first part ( before the ContinueWith) from each method , and then run the second part( after the ContinueWith) from each method.

Something like :

enter image description here

So i've look at the reference source for base.Sendasync : But couldn't find how base.Sendasync is doing this symmetrical execution

Question

How does base.Sendasync is doing that symmetrical execution without having one method calling the other?

Royi Namir
  • 144,742
  • 138
  • 468
  • 792

2 Answers2

4

Here is the console-appified Web API pipeline for you.

abstract class BaseHandler // HttpHandler
{
    public abstract Task MyMethodAsync();
}

abstract class Handler : BaseHandler // MessageHandler
{
    public Handler InnerHandler { get; set; }

    public override Task MyMethodAsync()
    {
        if (this.InnerHandler != null)
            return this.InnerHandler.MyMethodAsync();
        else
            return Task.FromResult<object>(null);
    }
}

class Handler1 : Handler
{
    public override async Task MyMethodAsync()
    {
        Console.WriteLine("Method_1"); //alias to request
        await base.MyMethodAsync();
        Console.WriteLine("Finished Method_1");  //alias to response
    }
}

class Handler2 : Handler
{
    public override async Task MyMethodAsync()
    {
        Console.WriteLine("Method_2"); //alias to request
        await base.MyMethodAsync();
        Console.WriteLine("Finished Method_2");  //alias to response
    }
}

class LastHandler : Handler
{
    public override async Task MyMethodAsync()
    {
        // Does nothing
        await base.MyMethodAsync();
    }
}

class Program
{
    static void Main(string[] args)
    {
        List<Handler> handlers = new List<Handler>();
        // You do this when you add the handler to config
        handlers.Add(new Handler1());
        handlers.Add(new Handler2());

        // This part is done by HttpClientFactory
        Handler pipeline = new LastHandler();

        handlers.Reverse();
        foreach (var handler in handlers)
        {
            handler.InnerHandler = pipeline;
            pipeline = handler;
        }

        pipeline.MyMethodAsync().Wait();
    }
}
  • Very helpful , shows exactly the chaining... but how could I know it from the reference source ? ALL I see is abstracts without implementations, so how did you find it :-) ? I was looking at DelegatingHandler – Royi Namir Oct 18 '14 at 20:05
  • The first component in Web API pipeline is `HttpServer`. https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/System.Web.Http/HttpServer.cs. In `Initialize`, `HttpClientFactory.CreatePipeline` is called that builds the pipeline. https://github.com/ASP-NET-MVC/aspnetwebstack/blob/0ee98f84b41b6bef6bde6945e2797de9dac7d48a/src/System.Net.Http.Formatting/HttpClientFactory.cs. `HttpControllerHandler` calls `HttpServer.SendAsync`. https://github.com/ASP-NET-MVC/aspnetwebstack/blob/0ee98f84b41b6bef6bde6945e2797de9dac7d48a/src/System.Web.Http.WebHost/HttpControllerHandler.cs – Badrinarayanan Lakshmiraghavan Oct 19 '14 at 02:32
  • DelegatingHandler does not do much but calling SendAsync on the inner handler. Pipeline is built in HttpServer and HttpControllerHandler calls HttpServer. HttpControllerHandler is a web-hosting concept BTW. – Badrinarayanan Lakshmiraghavan Oct 19 '14 at 02:34
  • PS how can [it be a source code](http://referencesource.microsoft.com/#System.Net.Http/System/Net/Http/DelegatingHandler.cs) if it doesn't compile ?( for example : `protected DelegatingHandler();` has no body...) – Royi Namir Oct 19 '14 at 06:52
3

Each delegating handler is aware of its "next" handler, and DelegatingHandler.SendAsync does call SendAsync on the next ("inner") handler. You can think of it like a linked list, as such:

public abstract class MyDelegatingHandler
{
  private readonly MyDelegatingHandler _next;

  public MyDelegatingHandler(MyDelegatingHandler next = null)
  {
    _next = next;
  }

  public virtual Task SendAsync()
  {
    if (_next == null)
      return Task.FromResult(0);
    return _next.SendAsync();
  }
}
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Thanks. but where is the _chaining_ is done ? can you please elaborate your code to show how chaining is done ? if there's another `MyDelegatingHandler` implementer - it has its own `_next` so I dont see where is the link here. – Royi Namir Oct 18 '14 at 13:37
  • 1
    It's being done in (or by) `config.MessageHandlers.Add`. To confirm that, try adding them in the opposite order. – Mark Hurd Oct 18 '14 at 13:43
  • @RoyiNamir: The actual `DelegatingHandler` just has an `InnerHandler` property that is set by the framework. In my example code, it's set by the constructor. – Stephen Cleary Oct 18 '14 at 16:02