Web Api - Routing in Depth

This week I had to use dotPeek on the Web Api to understand how the framework selects the controller method (action) that gets executed. The process is not complicated but I thought it might help other people if I documented my findings.

There's also a post on the asp.net website that talks about the web api rounting, check it out here.

The diagram below tries do describe the decision process behind selecting the method of the controller that will get executed. I've added a little extra documentation through the annotations on the diagram.

1 Action Based

If the route defines the action parameter then the method selection will be based on the value of that parameter. The matching will be based on the method name or the method alias (A method in the controller may be aliased using the ActionName attribute).
All the matching methods are then filtered by verb. The filtering is done by attributes applied to the method (such as HttpGet). Only the methods that match the verb of the incoming request will be returned. Methods with no attributes will also be considered valid as they don’t have any attributes to allow filtration.

2 Verb Based

When the method selection is verb based we need to get the Http verb used in the request. The controller methods are then filtered in two ways:

  • Get all the methods annotated with http verb attributes matching the http method in the request (attributes such as HttpGet or HttpPost);
  • Get all the methods that have the prefix matching the http method in the request (PostBooks matches the Post verb);

3 Check Results Found

All the candidate methods returned by either the verb based or action based approach are then analyzed. If no candidates where found an exception is thrown. If multiple methods were found it’s necessary to analyze it’s parameters to find out the best possible fit.

4 Find Route by Parameters

The parameters from the route and the parameters from the query string are compared to the method's parameters. If no parameters are found in the query string or in the route, all methods without parameters will be selected. If multiple methods match the parameters found, the ones that take the most parameters will win. Here is a diagram that details this specific part of the process:

 

5 Selection Filters

The last filters executed on the candidate methods will make sure that all methods marked with the NonAction attribute will be excluded.
 

I hope these diagrams were clear enough to help understand better the process of selecting the controller method that gets executed for a request.

Web Api - Testing with HttpClient

The Web Api is a project that has been on my radar since I first saw Glenn Block’s presentation last year on MIX. I was only recently though, when we decided to really take on REST at work that I really started looking into with in depth.

One aspect that caught my attention was the HttpClient. I’ve been using RestSharp for a while and I have to say that I am really happy with it. I still wanted to test the new HttpClient and see how it compared to RestSharp.

So let’s say I want to call a super duper service that returns a Guid. Here is a VERY simple example of how I would implement a client using RestSharp:

public class GuidClient
{
    private readonly IRestClient _client;

    public GuidClient(IRestClient client)
    {
        _client = client;
    }

    public string Execute()
    {
        var request = new RestRequest();

        RestResponse response = _client.Execute(request);

        if (response.StatusCode != HttpStatusCode.OK)
        {
            throw new Exception("Invalid response");
        }
        
        return response.Content;
    }
}

As I said, it's a very simple example. The important part here is that if I get a valid response I want to return the content (which should be the GUID), otherwise I want to throw an Exception. Here are the tests:

[TestFixture]
class GreetingClientTest
{
    [Test]
    public void Throws_exception_if_response_not_OK()
    {
        var mock = new Mock<IRestClient>();
        mock.Setup(x => x.Execute(It.IsAny<IRestRequest>()))
            .Returns(new RestResponse {StatusCode = HttpStatusCode.BadRequest});

        var client = new GuidClient(mock.Object);
        Assert.Throws<Exception>(() => client.Execute());
    }

    [Test]
    public void Returns_content_if_response_is_OK()
    {
        string content = Guid.NewGuid().ToString();
        var mock = new Mock<IRestClient>();
        mock.Setup(x => x.Execute(It.IsAny<IRestRequest>()))
            .Returns(new RestResponse
                         {
                             StatusCode = HttpStatusCode.OK,
                             Content = content
                         });

        var client = new GuidClient(mock.Object);
        var result = client.Execute();
        Assert.AreEqual(content, result);
    }
}

It’s easy to notice how simple the RestSharp abstractions make our job of testing. Mock the IRestClient to return the desired RestResponse and it's good to go.

When creating my first client using the HttpClient I wanted to pass the HttpClient as a constructor parameter in the same manner I did with IRestClient. That’s when I noticed that the HttpClient doesn’t implement any interfaces other than IDisposable. Hum, no interfaces? So how can I mock this thing? Good thing Glenn Block was ready to help on twitter:

“@gblock: @perezgb @howard_dierking with web api you can pass a fake message handler to the client to test.”

So Glenn also sent me the code from one of his talks where he creates a fake handler in order to help testing with the HttpClient. So taking his code I created an HttpClient version of my service and tests:

public class GuidHttpClient
{
    private readonly HttpClient _client;

    public GuidHttpClient(HttpClient client)
    {
        _client = client;
    }

    public string Execute()
    {
        var request = new HttpRequestMessage { RequestUri = new Uri("http://localhost/guidservice") };
        Task<HttpResponseMessage> task = _client.SendAsync(request);
        HttpResponseMessage response = task.Result;
        if (response.StatusCode != HttpStatusCode.OK)
        {
            throw new Exception("Invalid response");
        }
        return response.Content.ReadAsStringAsync().Result;
    }
}

And here are the tests:

[TestFixture]
public class GuidHttpClientTest
{
    [Test]
    public void Throws_exception_if_response_not_OK()
    {
        var response = new HttpResponseMessage(HttpStatusCode.BadRequest);
        var httpClient = new HttpClient(new FakeHandler
                                            {
                                                Response = response,
                                                InnerHandler = new HttpClientHandler()
                                            });

        var client = new GuidHttpClient(httpClient);
        Assert.Throws<Exception>(() => client.Execute());
    }

    [Test]
    public void Returns_content_if_response_is_OK()
    {
        string content = Guid.NewGuid().ToString();
        var response = new HttpResponseMessage(HttpStatusCode.OK);
        response.Content = new StringContent(content);

        var httpClient = new HttpClient(new FakeHandler
        {
            Response = response,
            InnerHandler = new HttpClientHandler()
        });

        var client = new GuidHttpClient(httpClient);
        string result = client.Execute();
        Assert.AreEqual(content, result);
    }
}

And this is the fake message handler:

public class FakeHandler : DelegatingHandler
{
    public HttpResponseMessage Response { get; set; }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
                                                           CancellationToken cancellationToken)
    {
        if (Response == null)
        {
            return base.SendAsync(request, cancellationToken);
        }

        return Task.Factory.StartNew(() => Response);
    }
}

Ok, so mission accomplished! I was able to write my service and tests using the HttpClient. One thing I really like is the russian doll model, kinda like the one you can find on FubuMVC, that the DelegatingHandler makes possible. On the other hand, I still like my RestSharp tests better, they seem cleaner but maybe that’s just me. It's the way I've been writing code for a while and I feel comfortable with it. Some times though, we have to push ourselves out of our comfort zone and try different things, right?

Please take all this with a grain of salt as I don’t consider myself to be any kind of expert in the ASP.NET Web Api. Also this is only my first stab at the HttpClient, I plan to keep going with my tests. I'll let you know if I come across anything interesting ;-)