0

Considering that message handlers return from their execution using SendAsync, such as:

var response = await base.SendAsync(request, cancellationToken);

does this mean that each message handler cannot guarantee that a previous message handler will have completed it's work prior?

For example, if B in the following list of handlers is slowest to complete, would the execution begin like this:

A, B, C

But return like this:

A, C, B

If so, does this also mean that the controller will not be executed until all the handlers have completed, or could the controller begin execution before the request object is properly populated?

Matt W
  • 11,753
  • 25
  • 118
  • 215

1 Answers1

0

In the case of delegating message handlers, each one starts the next one by calling base.SendAsync and then (asynchronously) waits for it to complete by using await. So, they can handle both pre- and post- conditions.

The normal code works just as you'd expect:

// At this point, no later handlers (or the controller) have executed.
// All earlier handlers have run up until the point they call base.SendAsync.
var response = await base.SendAsync(request, cancellationToken);
// At this point, all later handlers (and the controller) have executed and completed.
// All earlier handlers are (asynchronously) waiting for you to complete.

It is not possible for C to complete before B, because B is awaiting SendAsync on C, so B will not complete until after C completes.

Remember that message handlers are bidirectional. They're split up into the "pre" part (before the base.SendAsync call) and the "post" part (after the await of the task returned from base.SendAsync). So, when your handler's SendAsync is invoked, you know that all the "pre" parts of earlier filters have already run, but none of their "post" parts. Same for the controller: all "pre" parts of all filters have run, but none of the "post" parts.

Now, if you were to do something bad, like not await the task returned from base.SendAsync, then you won't get the nice behavior above and things get complicated. But the vast majority of the time, your handler code does an await base.SendAsync, and is split cleanly into "pre" and "post" parts.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Thank you. Is this the same as the code you posted: return base.SendAsync(request, cancellationToken).ContinueWith(task => CheckToken(token,task.Result), cancellationToken); – Matt W Sep 16 '14 at 14:27
  • @MattW: You should use `await` instead of `ContinueWith`. – Stephen Cleary Sep 16 '14 at 14:29
  • Other than syntax, is there a reason for that? – Matt W Sep 16 '14 at 14:34
  • Also, does the code executing in the ContinueWith portion execute while the inner handlers execute? – Matt W Sep 16 '14 at 14:35
  • This page implies that ContinueWith is not available prior to .NET 4.5: http://www.asp.net/web-api/overview/advanced/http-message-handlers – Matt W Sep 16 '14 at 14:38
  • @MattW: `ContinueWith` was introduced with .NET 4.0. I recommend against `ContinueWith` because it has some [confusing default behavior, as I describe on my blog](http://blog.stephencleary.com/2013/10/continuewith-is-dangerous-too.html). Also, `await` by default will ensure the continuation code executes in the same request context. Your `ContinueWith` example will execute after the inner handlers complete, but it will run on a thread pool thread outside the request context. – Stephen Cleary Sep 16 '14 at 15:46