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).
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 :
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 :
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?
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();
}
}