Complex Type Action Parameters and Controller Action Selection with ASP.NET Web API

How to use complex type action parameters in ASP.NET Web API and involve them inside the controller action selection logic
2012-09-30 19:04
Tugberk Ugurlu


If you are familiar with ASP.NET MVC and trying to find your way with ASP.NET Web API, you may have noticed that the default action selection logic with ASP.NET Web API is pretty different than the ASP.NET MVC's. First of all, the action parameters play a huge role on action selection in ASP.NET Web API. Consider the following controller and its two action methods:

public class CarsController : ApiController { 

    //GET /api/cars?categoryId=10
    public string[] GetCarsByCategoryId(int categoryId) { 
        
        return new[] { 
            "Car 1",
            "Car 2",
            "Car 3"
        };
    }
    
    //GET /api/cars?colorId=10
    public string[] GetCarsByColorId(int colorId) { 
        
        return new[] { 
            "Car 1",
            "Car 2"
        };
    }
}

This doesn’t going to cause the action ambiguity because the action parameter names are different. The default action selector (ApiControllerActionSelector) going to extract the action parameter names and try to match those with the URI parameters such as query string and route values. So if a GET request comes to /api/cars?categoryId=10, the GetCarsByCategoryId action method will be invoked. If a GET request comes to /api/cars?colorId=10 in this case, the GetCarsByColorId action method will be called.

It's possible to use complex types as action parameters for GET requests and bind the route and query string values by marking the complex type parameters with FromUriAttribute. However, the default action selection logic only considers simple types which are System.String, System.DateTime, System.Decimal, System.Guid, System.DateTimeOffset and System.TimeSpan. For example, if you have GetCars(Foo foo) and GetCars(Bar bar) methods inside your controller, you will get the ambiguous action error as the complex types are completely ignored by the ApiControllerActionSelector.

Let’s take the following as example here:

public class CarsByCategoryRequestCommand {

    public int CategoryId { get; set; }
    public int Page { get; set; }

    [Range(1, 50)]
    public int Take { get; set; }
}

public class CarsByColorRequestCommand {

    public int ColorId { get; set; }
    public int Page { get; set; }

    [Range(1, 50)]
    public int Take { get; set; }
}

public class CarsController : ApiController {

    public string[] GetCarsByCategoryId(
        [FromUri]CarsByCategoryRequestCommand cmd) {

        return new[] { 
            "Car 1",
            "Car 2",
            "Car 3"
        };
    }

    public string[] GetCarsByColorId(
        [FromUri]CarsByColorRequestCommand cmd) {

        return new[] { 
            "Car 1",
            "Car 2"
        };
    }
}

We are not performing any logic inside the action here but you can understand from the action parameter types that we are aiming to perform pagination here. So, we are receiving the inputs from the consumer. We can use simple types directy as action parameters but there is no built-in way to validate the simple types and I haven’t found an elegant way to hook something up for that. As a result, complex type action parameters comes in handy in such cases.

If we now send a GET request to /api/cars?colorId=23&page=2&take=12, we would get the ambiguity error message:

image

To workaround this issue, I created a new action selector which has the same implementation as the ApiControllerActionSelector and a few tweaks to make this feature work. It wasn’t easy at all. The ApiControllerActionSelector is not so extensible and I had to manually rewrite it (honestly, I didn’t directly copy-paste. I rewrote the every single line). I also thought that this could make it into the framework. So, I sent a pull request which got rejected:  3338. There is also an issue open to make the default action selector more extensible: #277. I encourage you to go and vote!

So, what can we do for now to make this work? Go and install the latest WebAPIDoodle package from the Official NuGet feed:

PM> Install-Package WebAPIDoodle

This package has a few useful components for ASP.NET Web API and one of them is the ComplexTypeAwareActionSelector. First of all, we need to replace the default action selector with our ComplexTypeAwareActionSelector as below. Note that ComplexTypeAwareActionSelector preserves all the features of the ApiControllerAction selector.

protected void Application_Start(object sender, EventArgs e) {

    var config = GlobalConfiguration.Configuration;
    config.Routes.MapHttpRoute(
        "DefaultApiRoute",
        "api/{controller}/{id}",
        new { id = RouteParameter.Optional }
    );

    // Replace the default action IHttpActionSelector with
    // WebAPIDoodle.Controllers.ComplexTypeAwareActionSelector
    config.Services.Replace(
        typeof(IHttpActionSelector),
        new ComplexTypeAwareActionSelector());
}

This package also contains an attribute named UriParametersAttribute which accepts a params string[] parameter. We can apply this attribute to action methods and pass the parameters that we want to be considered during the action selection. The below one shows the sample usage for our above case:

public class CarsController : ApiController {

    [UriParameters("CategoryId", "Page", "Take")]
    public string[] GetCarsByCategoryId(
        [FromUri]CarsByCategoryRequestCommand cmd) {

        return new[] { 
            "Car 1",
            "Car 2",
            "Car 3"
        };
    }

    [UriParameters("ColorId", "Page", "Take")]
    public string[] GetCarsByColorId(
        [FromUri]CarsByColorRequestCommand cmd) {

        return new[] { 
            "Car 1",
            "Car 2"
        };
    }
}

If we now send the proper GET requests as below, we should see it working:

image

image

You can also grab the sample to see this in action. I should also mention that I am not saying that this is the way to go. Clearly, this generates a lot of noise and we can do better here. The one solution would be to inspect the simple type properties of the complex type action parameter without needing the UriParametersAttribute.

Asynchronous .NET Client Libraries for Your HTTP API and Awareness of async/await's Bad Effects

Writing asynchronous .NET Client libraries for your HTTP API and using asynchronous language features (aka async/await) and some deadlock issue you might face.
2012-09-21 06:34
Tugberk Ugurlu


Haven’t you shot yourself in the foot yet with async/await? If not, you are about to if you are writing a client library for your newly created ASP.NET Web API application with .NET 4.5 using new asynchronous language features.

I wrote a blog post couple of months ago on the importance of Current SynchronizationContext and the new C# 5.0 asynchronous language Features (aka async/await). I wrote that post just after I watched the The zen of async: Best practices for best performance talk of Stephen Toub on //Build 2011 and that was one of the best sessions that I was and still am glad to watch. I learnt so many things from that session and some of them was amazingly important.

Filip Ekberg also has a very nice and useful blog post which has an identical title as my previous post on the topic: Avoid shooting yourself in the foot with Tasks and Async.

The post was pointing out that it is extremely easy to end up with a deadlock if you are not careful enough. The post explains every details but if want to recap shortly, here it is:

When you are awaiting on a method with await keyword, compiler generates bunch of code in behalf of you. One of the purposes of this action is to handle synchronization with the UI thread. The key component of this feature is theSynchronizationContext.Current which gets the synchronization context for the current thread. SynchronizationContext.Current is populated depending on the environment you are in. The GetAwaiter method of Task looks up for SynchronizationContext.Current. If current synchronization context is not null, the continuation that gets passed to that awaiter will get posted back to that synchronization context.

When consuming a method, which uses the new asynchronous language features, in a blocking fashion, you will end up with a deadlock if you have an available SynchronizationContext. When you are consuming such methods in a blocking fashion (waiting on the Task with Wait method or taking the result directly from the Result property of the Task), you will block the main thread at the same time. When eventually the Task completes inside that method in the threadpool, it is going to invoke the continuation to post back to the main thread because SynchronizationContext.Current is available and captured. But there is a problem here: the UI thread is blocked and you have a deadlock!

The post also has a sample but I want dig deep into this. As you all know, ASP.NET Web API RTW version has been shipped couple of weeks ago and we may have started to take advantage of this framework to build our HTTP-based lightweight APIs. You might be using another framework to create your APIs or you might be maintaining an existing one. No matter which category you put yourself in, you are likely to build platform specific client libraries for your API. If you are going to target .NET framework, I am 90% sure that you want your client library to be asynchronous and new HttpClient will just give you that option.

I have started writing a simple blog engine for myself called MvcBloggy nearly a year ago and after the RTW release of the ASP.NET MVC 4 and ASP.NET Web API, I decided to try something different and expose the data through HTTP. So, my ASP.NET MVC application won’t know anything about where my data is store or how I retrieve it. It is just going to consume the HTTP APIs. The application is shaping up nicely and I even started to build my .NET client for the API as well.

Couple of days ago, Ben Foster (a brilliant developer) raised a question on Twitter about consuming asynchronous methods on ASP.NET Web Pages application and I also wondered about that. Then, we looked around a bit and also contacted with Erik Porter (@HumanCompiler), Program Manager on the ASP.NET team, and he confirmed that it is not possible today. He encouraged us to file an issue and we did (#418). This got me thinking about my little project’s .NET client, though. What if I wanted to create a blog web site with ASP.NET Web Pages by consuming the .NET client of my blog engine? I have no option rather than consuming the methods in a blocking fashion but with my current implementation, I am not able to do that because I will end up with deadlocks. Let me prove that to you with a little example which you can also find up on GitHub: AsyncAwaitForLibraryAuthors.

Assuming we have a little API which returns back a list of cars and we want to build a .NET client to consume the HTTP API and abstracts all the lower level HTTP stuff away. The one that I created is as below (it is a simple one for the demo purposes):

public class Car {

    public int Id { get; set; }
    public string Make { get; set; }
    public string Model { get; set; }
    public int Year { get; set; }
    public float Price { get; set; }
}

public class SampleAPIClient {

    private const string ApiUri = "http://localhost:17257/api/cars";

    public async Task<IEnumerable<Car>> GetCarsAsync() {

        using (HttpClient client = new HttpClient()) {

            var response = await client.GetAsync(ApiUri);

            // Not the best way to handle it but will do the work for demo purposes
            response.EnsureSuccessStatusCode();
            return await response.Content.ReadAsAsync<IEnumerable<Car>>();
        }
    }
}

We leveraged the new asynchronous language features and we were able to write a small amount of code to get the job done. More importantly, we are making the network call and the deserialization asynchronously. As we know from the earlier sentences that if there is a SynchronizationContext available for us, the code that the compiler is generating for us will capture that and post the continuation back to that context to be executed. Keep this part in mind. I put this little class inside a separate project called SampleAPI.Client and reference this from my web clients. I have created two clients: one ASP.NET MVC and one ASP.NET Web Pages applications.

In my ASP.NET MVC 4 application, I have a controller which has two actions. One of these actions will call the API asynchronously and one of will do the same by blocking:

public class HomeController : Controller {

    public async Task<ViewResult> CarsAsync() {

        SampleAPIClient client = new SampleAPIClient();
        var cars = await client.GetCarsAsync();

        return View("Index", model: cars);
    }

    public ViewResult CarsSync() {

        SampleAPIClient client = new SampleAPIClient();
        var cars = client.GetCarsAsync().Result;

        return View("Index", model: cars);
    }
}

Our view is also so simple as follows:

@model IEnumerable<SampleAPI.Client.Car>
@{
    ViewBag.Title = "Home Page";
}

<h3>Cars List</h3>

<ul>
    @foreach (var car in Model) {
        <li>
            @car.Make, @car.Model (@car.Year) - @car.Price.ToString("C")
        </li>    
    }
</ul>

When we navigate to /home/CarsAsync, we will get back the result.

image

However, when we navigate to /home/CarsSync to invoke the CarsSync method, we will see that the page will never come back because we just introduced a deadlock due to the reasons we have explained earlier. Let’s have a look at the Web Pages sample:

@{
    Layout = "~/_SiteLayout.cshtml";
    Page.Title = "Home Page";

    SampleAPI.Client.SampleAPIClient client = new SampleAPI.Client.SampleAPIClient();
    var cars = client.GetCarsAsync().Result;
}

<h3>Cars List</h3>

<ul>
    @foreach (var car in cars) {
        <li>
            @car.Make, @car.Model (@car.Year) - @car.Price.ToString("C")
        </li>    
    }
</ul>

This page is also not going to respond because Web Pages runs under ASP.NET and ASP.NET has a SynchronizationContext available.

When we take a look at our GetCarsAsync method implementation, we will see that it is completely unnecessary for us to get back to current SynchronizationContext because we don’t need anything from the current context. This is good because it is not our (I mean our .NET client’s) concern to do anything under the current SynchronizationContext. It is, on the other hand, our consumer’s responsibility. Stephen Toub said something in his talk on //Build 2011 and the words not the same but it expresses the meaning of the below sentences:

If you are a library developer, the default behavior which await gives you is nearly never what you want. However, if you are a application developer, the default behavior will nearly always what you want.

I, again, encourage you to check that video out.

The solution here is simple. When we are creating our libraries, we just need to be more careful and think about the usage scenarios. In our case here, we need to suppress the default SynchronizationContext behavior that the compiler is generating for us. We can achieve this with ConfigureAwait method of the Task class which was introduced with .NET 4.5. The ConfigureAwait method accepts a Boolean parameter named as continueOnCapturedContext. We can pass false into this method not to marshal the continuation back to the original context captured and our problem would be solved. Here is the new look of our .NET client for our HTTP API.

public class SampleAPIClient {

    private const string ApiUri = "http://localhost:17257/api/cars";

    public async Task<IEnumerable<Car>> GetCarsAsync() {

        using (HttpClient client = new HttpClient()) {

            var response = await client.GetAsync(ApiUri)
                .ConfigureAwait(continueOnCapturedContext: false);

            // Not the best way to handle it but will do the work for demo purposes
            response.EnsureSuccessStatusCode();
            return await response.Content.ReadAsAsync<IEnumerable<Car>>()
                .ConfigureAwait(continueOnCapturedContext: false);
        }
    }
}

When we now run our Web Pages application, we will see the web site working nicely (same is also applicable for the CarsSync action method of our ASP.NET MVC application).

image

If you are going to write a .NET client for your company’s big HTTP API using new asynchronous language features, you might want to consider these facts before moving on. Otherwise, your consumers will have hard time understanding what is really going wrong.

Script Out Everything - Initialize Your Windows Azure VM for Your Web Server with IIS, Web Deploy and Other Stuff

Script Out Everything - Initialize Your Windows Azure VM for Your Web Server with IIS, Web Deploy and Other Stuff
2012-09-17 20:17
Tugberk Ugurlu


Today, I am officially sick and tired of initializing a new Web Server every time so I decided to script it all out as much as I can. I created a new Windows Azure VM running Windows Server 2012 and installed Web Platform Installer v4 Command Line tool (aka WebPICMD.exe).

Next thing I need to do is to install the IIS Web Server Role inside my VM. To do that, I opened up the Server Manager and closed it instantly because I didn't wanna do that through a GUI either. It turned out that it is fairly easy to manage your server roles and features through PowerShell thanks to ServerManager PowerShell module. This module has couple of handy Cmdlets which enable you to manage your server’s roles and features.

image

To install the IIS Web Server, I run the following PowerShell command. It installs IIS Web Server (along with the necessary dependencies and management tools) and logs the output under the TEMP folder.

$logLabel = $((get-date).ToString("yyyyMMddHHmmss"))
Import-Module -Name ServerManager
Install-WindowsFeature -Name Web-Server -IncludeManagementTools -LogPath "$env:TEMP\init-webservervm_webserver_install_log_$logLabel.txt"

image

When we look at the installed features, we should see that IIS Web Server is now listed there:

image

At this stage, we have a few more necessary features to install such as ASP.NET 4.5 and Management Service. Optionally, I always want to install Dynamic Content Compression and IIS Management Scripts and Tools features. To install those features, we will run the following script:

#add additional windows features
$additionalFeatures = @('Web-Mgmt-Service', 'Web-Asp-Net45', 'Web-Dyn-Compression', 'Web-Scripting-Tools')
foreach($feature in $additionalFeatures) { 
    
    if(!(Get-WindowsFeature | where { $_.Name -eq $feature }).Installed) { 

        Install-WindowsFeature -Name $feature -LogPath "$env:TEMP\init-webservervm_feature_$($feature)_install_log_$((get-date).ToString("yyyyMMddHHmmss")).txt"   
    }
}
#Set WMSvc to Automatic Startup
Set-Service -Name WMSvc -StartupType Automatic

#Check if WMSvc (Web Management Service) is running
if((Get-Service WMSvc).Status -ne 'Running') { 
    Start-Service WMSvc
}

As you can see at the end of the script, we are also setting the Management Service's startup type to automatic and finally, we are starting the service.

Now the IIS Web Server is ready, we need to get a few more bits through the Web Platform Installer v4 Command Line tool. With all of my web servers, there are two concrete tools I would like to have: Web Deploy and URL Rewrite Module. We can certainly install those manually but we can also script out their installation. WebPICMD.exe allows us to install products through command line and the following command will work for us:

$webPiProducts = @('WDeployPS', 'UrlRewrite2')
.\WebPICMD.exe /Install /Products:"$($webPiProducts -join ',')" /AcceptEULA /Log:"$env:TEMP\webpi_products_install_log_$((get-date).ToString("yyyyMMddHHmmss")).txt"

I assume that WebPICMD.exe is under your path here (if you have installed the 64x version of the product, you can find the executable file under C:\Program Files\Microsoft\Web Platform Installer). When the installation is complete, we should see the success message:

image

Well, we have completed most of the work but there are still couple of things to do. First of all, we need to allow incoming connections through TCP port 8172 because this is the port that Web Deploy will talk through. To enable that, we can go to Windows Firewall with Advanced Security window but that would be lame. Are we gonna use netsh? Certainly not :) With PowerShell 3.0, we can now control the Windows Firewall with Advanced Security Administration. This functionality is provided through NetSecurity PowerShell module but with the new dynamic module loading feature of PowerShell 3.0, we don’t need to separately import this. The following command will add the proper firewall rule to our server.

New-NetFirewallRule -DisplayName "Allow IIS Management Service In" -Direction Inbound -LocalPort 8172 -Protocol TCP -Action Allow

Lastly, we need to inform windows azure about this firewall rule as well because all the requests, which come from outside, will go through the load balancer and it doesn’t open up any ports by default (except for Remote Desktop ports). You can the endpoints through Windows Azure Portal but Windows Azure PowerShell Cmdlets to add this as well. The following command will add the proper rule. Just change the $serviceName and $vmName according to your credentials:

Get-AzureVM -ServiceName $serviceName -Name $vmName | Add-AzureEndpoint -Name "WebDeploy" -Protocol TCP -LocalPort 8172 -PublicPort 8172 | Update-AzureVM

When we look at the portal, we should see that our endpoint was created.

AzureEndpoint

Now, everything should be working perfectly. Of course you also need to add TCP port 80 to your Endpoint lists for your VM in order for your web sites to be reachable through HTTP (assuming you will only use PORT 80 for your web applications). To test everything out, I created a web application under IIS. Then, I right clicked on it and navigate to Deploy > Configure Web Deploy Publishing.

image

This will bring up another dialog. This is the place where we can configure web deploy settings. There are other ways to do this as well.

image

Just change the VM name with your VIP address and this will generate a publish profile. We can now use this publish profile file to push our web application to the server.

WebDeployPublishVS

When the publish is completed, we will see the complete result inside the Output window.

WebDeployPublishVS_2

We jumped through lots of hoops to get this done but how is this better than doing it manually? Imagine that you bring all these scripts together and run them through multiple VMs. It’s going to save a lot of time for you. I am not an IT pro. So, this is enough to make me happy because I just proved that nearly everything that a web developer needs can be automated through PowerShell or various command line tools.

Update:

I put these together inside one script file. Follow the instructions inside the script and you will be good to go.

https://gist.github.com/3742921#file_init_web_server_vm.ps1

This doesn't add the endpoints for your VM. For this, you have another script as well:

https://gist.github.com/3742921#file_init_web_server_azure_vm_endpoints.ps1

ASP.NET Web API and Handling ModelState Validation

How to handle ModelState Validation errors in ASP.NET Web API with an Action Filter and HttpError object
2012-09-11 10:39
Tugberk Ugurlu


@amirrajan, who thinks that Web API sucks :), wanted to see how model validation works in ASP.NET Web API. Instead of responding him directly, I decided to blog about it. In his post, he indicated that we shouldn’t use ASP.NET Web API because it has usability issues but I am sure that he hasn’t taken the time to really understand the whole framework. It is really obvious that the blog post is written by someone who doesn’t know much about the framework. This is not a bad thing. You don’t have to know about every single technology but if *you* express that the technology bad, you better know about it. Anyway, this is not the topic.

ASP.NET Web API supports validation attributes from .NET Data Annotations out of the box and same ModelState concept which is present all across the ASP.NET is applicable here as well. You can inspect the ModelState property to see the errors inside your controller action but I bet that you want to just terminate the request and return back a error response if the ModelState is invalid. To do that, you just need to create and register an action filer as below:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class InvalidModelStateFilterAttribute : ActionFilterAttribute {

    public override void OnActionExecuting(HttpActionContext actionContext) {

        if (!actionContext.ModelState.IsValid) {

            actionContext.Response = actionContext.Request.CreateErrorResponse(
                HttpStatusCode.BadRequest, actionContext.ModelState);
        }
    }
}

This is one time code and you don’t need to write this over and over again. CreateErrorResponse extension method for HttpRequestMessage uses HttpError class under the covers to create a consistent looking error response. The key point here is that the content negotiation will be handled here as well. the conneg will be handled according to the content negotiator service that you registered. If you don’t register one, the default one (DefaultContentNegotiator) will kick in which will be fine for 90% of the time.

I created a tiny controller which simply exposes two GET and one POST endpoint:

public class CarsController : ApiController {

    private readonly CarsContext _carsCtx = new CarsContext();

    public IEnumerable<Car> GetCars() {

        return _carsCtx.All;
    }

    public Car GetCar(int id) {

        Tuple<bool, Car> carResult = _carsCtx.GetSingle(id);
        if (!carResult.Item1) {

            throw new HttpResponseException(HttpStatusCode.NotFound);
        }

        return carResult.Item2;
    }

    public HttpResponseMessage PostCar(Car car) {

        var addedCar = _carsCtx.Add(car);
        var response = Request.CreateResponse(HttpStatusCode.Created, car);
        response.Headers.Location = new Uri(
            Url.Link("DefaultApiRoute", new { id = addedCar.Id }));

        return response;
    }
}

Here is what our Car object looks like with the validation attributes:

public class Car {

    public int Id { get; set; }

    [Required]
    public string Make { get; set; }

    [Required]
    public string Model { get; set; }
    public int Year { get; set; }

    [Range(0, 200000)]
    public float Price { get; set; }
}

And I registered the filter globally inside the Application_Start method:

protected void Application_Start(object sender, EventArgs e) {

    var config = GlobalConfiguration.Configuration;
    config.Routes.MapHttpRoute(
        "DefaultApiRoute",
        "api/{controller}/{id}",
        new { id = RouteParameter.Optional }
    );

    config.Filters.Add(new InvalidModelStateFilterAttribute());
}

Let’s retrieve the list of cars first:

image

Let’s add a new car:

image

When we make a new request, it is now on the list:

image

Now, let’s try to add a new car again but this time, our object won’t fit the requirements and we should see the error response:

image

Here is the better look of the error message that we got:

image

Here is the plain text version of this message:

{
    "Message": "The request is invalid.",
    "ModelState": { 
        "car": [
            "Required property 'Make' not found in JSON. Path '', line 1, position 57."
        ],
        "car.Make" : [
            "The Make field is required."
        ], 
        "car.Price": [
            "The field Price must be between 0 and 200000."
        ]
    }
}

This doesn’t have to be in this format. You can play with the ModelState and iterate over the error messages to simply create your own error message format.

To be honest, this is no magic and this, by itself, doesn’t make ASP.NET Web API super awesome but please don’t make accusations on a technology without playing with it first at least enough amount of time to grasp the big picture. Otherwise, IMO, it would be a huge disrespect for the people who made the technology happen.

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

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
2012-09-08 08:46
Tugberk Ugurlu


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