rss
[ Log On ]

Dealing with Asynchrony inside the ASP.NET Web API HTTP Message Handlers

0
Comments
On 9/8/2012 11:46 AM by Tugberk
How to most efficiently deal with asynchrony inside ASP.NET Web API HTTP Message Handlers with TaskHelpers NuGet package or C# 5.0 asynchronous language features

ASP.NET Web API has a concept of Message Handlers for processing HTTP messages on both the client and server. If we take look under the hood inside the framework, we will see that Initialize method of the HttpServer instance is invoking CreatePipeline method of System.Net.Http.HttpClientFactory’s. CreatePipeline method accepts two parameters: HttpMessageHandler and IEnumerable<DelegatingHandler>.HttpServer.Initialize method is passing System.Web.Http.Dispatcher.HttpControllerDispatcher for HttpMessageHandlerparameter as the last HttpMessageHandler inside the chain and HttpConfiguration.MessageHandlers for IEnumerable<DelegatingHandler> parameter.

What happens inside the CreatePipeline method is very clever IMO:

public static HttpMessageHandler CreatePipeline(
    HttpMessageHandler innerHandler, 
    IEnumerable<DelegatingHandler> handlers) {
    
    if (innerHandler == null)
    {
        throw Error.ArgumentNull("innerHandler");
    }

    if (handlers == null)
    {
        return innerHandler;
    }

    // Wire handlers up in reverse order starting with the inner handler
    HttpMessageHandler pipeline = innerHandler;
    IEnumerable<DelegatingHandler> reversedHandlers = handlers.Reverse();
    foreach (DelegatingHandler handler in reversedHandlers)
    {
        if (handler == null)
        {
            throw Error.Argument("handlers", 
                Properties.Resources.DelegatingHandlerArrayContainsNullItem, 
                typeof(DelegatingHandler).Name);
        }

        if (handler.InnerHandler != null)
        {
            throw Error.Argument("handlers", 
                Properties.Resources
                .DelegatingHandlerArrayHasNonNullInnerHandler, 
                typeof(DelegatingHandler).Name, 
                "InnerHandler", 
                handler.GetType().Name);
        }

        handler.InnerHandler = pipeline;
        pipeline = handler;
    }

    return pipeline;
}

As you can see, the message handler order is reversed and the Matryoshka Doll is created but be careful here: it is ensured that HttpControllerDispatcher is the last message handler to run inside the chain. Also there is a bit of a misunderstanding about message handlers, IMHO, about calling the handler again on the way back to the client. This is actually not quite correct because the message handler’s SendAsync method itself won’t be called twice, the continuation delegate that you will chain onto SendAsync method will be invoked on the way back to client with the generated response message that you can play with.

For example, let's assume that the following two are the message handler:

public class XMagicMessageHandler : DelegatingHandler {

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, 
        CancellationToken cancellationToken) {
            
        //Play with the request here

        return base.SendAsync(request, cancellationToken)
            .ContinueWith(task => {

                //inspect the generated response
                var response = task.Result;

                //Add the X-Magic header
                response.Headers.Add("X-Magic", "ThisIsMagic");

                return response;
        });
    }
}

Besides this I have a tiny controller which serves cars list if the request is a GET request and I registered this message handler through the GlobalConfiguration object as below:

protected void Application_Start(object sender, EventArgs e) {

    var config = GlobalConfiguration.Configuration;
    config.Routes.MapHttpRoute("DefaultHttpRoute", "api/{controller}");
    config.MessageHandlers.Add(new XMagicMessageHandler());
    config.MessageHandlers.Add(new SecondMessageHandler());
}

When we send a request to /api/cars, the result should be similar to below one:

image

Worked great! But, there is very nasty bug inside our message handler? Can you spot it? OK, I’ll wait a minute for you...

Did u spot it? No! OK, no harm there. Let’s try to understand what it was. I added another message handler and that message handler runs after the XMagicMessageHandler but on the way back, it runs first. The continuation delegate for the message handler will throw DivideByZeroException exception on purpose.

public class SecondMessageHandler : DelegatingHandler {

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken) {

        return base.SendAsync(request, cancellationToken).ContinueWith(task => {

            throw new DivideByZeroException();

            return new HttpResponseMessage();
        });
    }
}

Now let’s put a breakpoint inside the continuation method of our XMagicMessageHandler and see what we got.

image

As you might expect, the Result property of the task doesn’t contain the response message generated by the controller. When we try to reach out to the Result property, the exception thrown by the SecondMessageHandler will be re-thrown here again and more importantly, IMO, your code doesn’t do what it is supposed to do. So, how do we get around this? Surely, you can put a try/catch block around the task.Result but that'd be a lame solution. The answer depends on what version of .NET Framework are you running on.

If you are running on .NET v4.0, things are a bit harder for you as you need to deal with TPL face to face. Thanks to ASP.NET team, it is now easier with TaskHelpers NuGet package. The TaskHelpers is actively being used by some major ASP.NET Frameworks internally at Microsoft such as ASP.NET Web API which embraces TPL from top to bottom all the way through the pipeline.

PM> Install-Package TaskHelpers.Sources

If you would like to learn more about TaskHelpers class and how it helps you, @bradwilson has a nice blog posts series on this topic and I listed the links to those posts under resources section at the bottom of this post. After installing the TaskHelpers package from NuGet, we need to fix the bugs inside our message handlers.

public class SecondMessageHandler : DelegatingHandler {

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken) {

        return base.SendAsync(request, cancellationToken).Then(response => {

            int left = 10, right = 0;
            var result = left / right;

            return response;

        }).Catch<HttpResponseMessage>(info => {

            var cacthResult = 
                new CatchInfoBase<Task<HttpResponseMessage>>.CatchResult();

            cacthResult.Task = TaskHelpers.FromResult(
                request.CreateErrorResponse(
                    HttpStatusCode.InternalServerError, info.Exception));

            return cacthResult;
        });
    }
}

So, what did we actually change? We now use Then method from the TaskHelpers package instead of directly using ContinueWith method. What Then method does is that it only runs the continuation if the Task’s status is RanToCompletion. So, if the base.SendAsync method here returns a faulted or cancelled task, the continuation delegate that we pass into the Then method won’t be invoked.

Secondly, we chain another method called Catch which only runs the continuation delegate if the task’s status is faulted. If the status is cancelled, the Catch method will return back a Task whose status is set to cancelled. Inside the continuation delegate, we construct a new HttpResponseMessage through CreateErrorResponse extension method for HttpRequestMessage by passing the response status and the exception. The exception details are only sent over the wire if the following conditions are met:

  • If you set GlobalConfiguration.Configuration.IncludeErrorDetailPolicy to IncludeErrorDetailPolicy.Always.
  • if you set GlobalConfiguration.Configuration.IncludeErrorDetailPolicy to IncludeErrorDetailPolicy.LocalOnly and you run your application locally.
  • If you set GlobalConfiguration.Configuration.IncludeErrorDetailPolicy to IncludeErrorDetailPolicy.Default and your host environment’s error policy allows you to expose error details (for ASP.NET hosting, customErrors element in the Web.config file).

The XMagicMessageHandler has been changed nearly the same way as SecondMessageHandler.

public class XMagicMessageHandler : DelegatingHandler {

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, 
        CancellationToken cancellationToken) {
            
        //Play with the request here

        return base.SendAsync(request, cancellationToken)
            .Then(response => {

                //Add the X-Magic header
                response.Headers.Add("X-Magic", "ThisIsMagic");
                return response;

            }).Catch<HttpResponseMessage>(info => {

                var cacthResult = 
                    new CatchInfoBase<Task<HttpResponseMessage>>.CatchResult();

                cacthResult.Task = TaskHelpers.FromResult(
                    request.CreateErrorResponse(
                        HttpStatusCode.InternalServerError, info.Exception));

                return cacthResult;
            });
    }
}

As I am running the application locally, I should see the error details if I send a request.

image

If I were to send a request with application/xml Accept header, I would get the error response back in xml format.

image

If you are on .NET v4.5, you will get lots of the things, which we’ve just done, out of the box. The Then and Catch extensions method from TaskHelpers package sort of mimic the new async/await compiler features some parts of the new async/await compiler features. So, the .NET v4.5 version of our message handlers look as below:

public class SecondMessageHandler : DelegatingHandler {

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken) {

        try {

            int left = 10, right = 0;
            var result = left / right;

            var response = await base.SendAsync(request, cancellationToken);
            return response;
        }
        catch (Exception ex) {

            return request.CreateErrorResponse(
                HttpStatusCode.InternalServerError, ex);
        }
    }
}
public class XMagicMessageHandler : DelegatingHandler {

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, 
        CancellationToken cancellationToken) {

        try {

            var response = await base.SendAsync(request, cancellationToken);
            response.Headers.Add("X-Magic", "ThisIsMagic");
            return response;
        }
        catch (Exception ex) {

            return request.CreateErrorResponse(
                HttpStatusCode.InternalServerError, ex);
        }
    }
}

I would like to clear out that the code that we wrote for .NET v4.0 would work just fine under .NET v4.5 but as you can see we get rid of all the noise here and have a nice and more readable code. The functionality also stays the same.

image

As expected stack trace is different but the result is the same. No matter which way you choose, just choose one. Don’t leave things to chance with message handlers!

Resources

Permalink Add to del.icio.usDigg!Share on Google BuzzShare on FacebookReddit!Stumble it!Share on Twitter

Why do I Need ASP.NET Web API When I Already Have ASP.NET MVC

0
Comments
On 8/30/2012 2:05 PM by Tugberk
Gleen Block explains why we need ASP.NET Web API when we already have ASP.NET MVC and what can we achieve with ASP.NET Web API

As the ASP.NET Web API framework becomes more and more popular, this discussion also gets more and more popular and becomes one of the endless discussions that exists among the .NET community. The simple reaction when most of the people try to understand what ASP.NET Web API does is "I can already do those things with ASP.NET MVC". Well, that’s not correct in my opinion because you make ASP.NET MVC do those things, it doesn’t do that out of the box.

There are several more reasons why ASP.NET Web API is a great choice for HTTP APIs and why we need a new framework for that along with ASP.NET MVC. Gleen Block has done a great job on explaining those in the following video.

Couldn’t be explained better I think.

Other Resources

Permalink Add to del.icio.usDigg!Share on Google BuzzShare on FacebookReddit!Stumble it!Share on Twitter

ASP.NET Web API Catch-All Route Parameter Binding

0
Comments
On 8/29/2012 4:14 PM by Tugberk
ASP.NET Web API has a concept of Catch-All routes but the frameowk doesn't automatically bind catch-all route values to a string array. Let's customize it with a custom HttpParameterBinding.

I just realized that ASP.NET Web API doesn’t bind catch-all route values as ASP.NET MVC does. If you are not familiar with catch all routing, Stephen Walter has a great explanation on his article under the "Using Catch-All Routes" section.

In ASP.NET MVC, when you have a route as below, you can retrieve the values of the catch all parameter as string array.

RouteTable.Routes.MapRoute(
    "CatchAllRoute",
    "blog/tags/{*tags}",
    new { controller = "blog", action = "tags" }
);

The controller action would look like as below:

public class BlogController : Controller {

    public ActionResult Tags(string[] tags) { 

        //...
    }
}

In ASP.NET Web API, we don’t have that capability. If we have a catch-all route, we could retrieve it as string and parse it manually but that would be so lame to do it inside the controller, isn’t it? There must be a better way. Well, there is! We can create a custom HttpParameterBinding and register it globally for string arrays. If you are interested in learning more about parameter binding in ASP.NET Web API, you might wanna have a look at Mike Stall’s WebAPI Parameter binding under the hood blog post. In our case, the custom HttpParameterBinding we want to create looks like as below:

public class CatchAllRouteParameterBinding : HttpParameterBinding {

    private readonly string _parameterName;
    private readonly char _delimiter;

    public CatchAllRouteParameterBinding(
        HttpParameterDescriptor descriptor, char delimiter) : base(descriptor) {

        _parameterName = descriptor.ParameterName;
        _delimiter = delimiter;
    }

    public override Task ExecuteBindingAsync(
        System.Web.Http.Metadata.ModelMetadataProvider metadataProvider,
        HttpActionContext actionContext,
        CancellationToken cancellationToken) {

        var routeValues = actionContext.ControllerContext.RouteData.Values;
            
        if (routeValues[_parameterName] != null) {

            string[] catchAllValues = 
                routeValues[_parameterName].ToString().Split(_delimiter);

            actionContext.ActionArguments.Add(_parameterName, catchAllValues);
        }
        else {

            actionContext.ActionArguments.Add(_parameterName, new string[0]);
        }

        return Task.FromResult(0);
    }
}

All the necessary information has been provided to us inside the ExecuteBindingAsync method. From there, we simply grab the values from the RouteData and see if there is any route value whose route parameter name is the same as the action method parameter name. If there is one, we go ahead and split the values using the delimiter char provided to us. If there is no, we just attach an empty string array for the parameter. At the end, we let our caller know that we are done by returning a pre-completed Task object. I was using .NET 4.5, so I simply used FromResult method of Task class. If you are on .NET 4.0, you can return a completed task by using TaskCompletionSource class.

The following code is the our catch-all route.

protected void Application_Start() {

    var config = GlobalConfiguration.Configuration;

    config.Routes.MapHttpRoute(
        "BlogpostTagsHttpApiRoute",
        "api/blogposts/tags/{*tags}",
        new { controller = "blogposttags" }
    );
}

The last thing is that we need to register a rule telling that if there is an action method parameter which is a type of string array, go ahead and use our custom HttpParameterBinding.

protected void Application_Start() {

    var config = GlobalConfiguration.Configuration;

    //...

    config.ParameterBindingRules.Add(typeof(string[]),
        descriptor => new CatchAllRouteParameterBinding(descriptor, '/'));
}

Now, if we send a request to /api/blogposts/tags/asp-net/asp-net-web-api, we would see that our action method parameter is bound.

image

So far so good but we might not want to register our HttpParameterBinding rule globally. Instead, we might want to specify it manually when we require it. Well, we can do that as well. We just need to create a ParameterBindingAttribute to get our custom HttpParameterBinding so that it will be used to bind the action method parameter.

public class BindCatchAllRouteAttribute : ParameterBindingAttribute {

    private readonly char _delimiter;

    public BindCatchAllRouteAttribute(char delimiter) {

        _delimiter = delimiter;
    }

    public override HttpParameterBinding GetBinding(HttpParameterDescriptor parameter) {

        return new CatchAllRouteParameterBinding(parameter, _delimiter);
    }
}

As you can see, it is dead simple. The only thing we need to do now is to apply this attribute to our action parameter:

public class BlogPostTagsController : ApiController {

    //GET /api/blogposts/tags/asp-net/asp-net-web-api
    public HttpResponseMessage Get([BindCatchAllRoute('/')]string[] tags) {

        //TODO: Do your thing here...

        return new HttpResponseMessage(HttpStatusCode.OK);
    }
}

When we send a request to /api/blogposts/tags/asp-net/asp-net-web-api, we shouldn’t see any difference.

image

I am still discovering how parameter and model binding works inside the ASP.NET Web API. So, there is a good chance that I did something wrong here :) If you spot it, please let me know :)

Permalink Add to del.icio.usDigg!Share on Google BuzzShare on FacebookReddit!Stumble it!Share on Twitter

Get the Start Navigation Sound Back on Windows 8

0
Comments
On 8/18/2012 5:15 AM by Tugberk
The usual "chlick" sound is not there when you navigate through inside the Windows Explorer and we will see how to get the Start Navigation sound back on Windows 8

I assume that you have probably started using Windows 8 RTM version as you are here checking out this blog post. If the answer is yes, you may have noticed that the usual "chlick" sound is not there when you navigate through inside the Windows Explorer (I am not sure if the preview versions of Windows 8 had this characteristic but if they do, they apparently didn’t bug me that much).

If you are a weird person just like me, it will eat you alive! Fortunately, it is not something hard to get it back. Just press Windows + W and type "system sounds". From the search results, click "Change system sounds" option as shown below.

Screenshot (3)

Then, the Sound configuration window will pop up and from the Sounds tab, find the File Explorer > Start Navigation event inside the Program Events list. Finally, you will see that it has no sound attached to it. Just find the Navigation Start.waw from the Sounds dropdown list and select it as shown below.

StartNavSound

When you apply the changes, you will get back the lovely sound back. I am not sure why they felt the need of leaving that out by default but the fix is easy as you have just seen.

Enjoy!

Permalink Add to del.icio.usDigg!Share on Google BuzzShare on FacebookReddit!Stumble it!Share on Twitter