Skip navigation

Source codes will be put on codeplex.com soon….

ASP.NET Web API Route Debugger

Over the time, I found around one  third of the user questions of Web API is about routing. Such as:

  1. http://stackoverflow.com/questions/13876816/web-api-routing
  2. http://stackoverflow.com/questions/13869541/why-is-my-message-handler-running-even-when-it-is-not-defined-in-webapi
  3. http://stackoverflow.com/questions/14058228/asp-net-web-api-no-action-was-found-on-the-controller
  4. http://stackoverflow.com/questions/13982896/versioning-web-api-actions-in-asp-net-mvc-4/14059654#14059654

There is no easy way to figure out how the route were decided and eventually everything retrogress into “guessing”.

I was inspired by Haack’s ASP.NET Route Debugger, and decided to build a route debugger for ASP.NET Web API. The tool should visualize the routing mechanism and exposing as much information as possible to help user design their routes easily.

User Instruction

Installation

You can install Web API route debugger from NuGet

PM> Install-Package WebApiRouteDebugger

If you don’t want to find the package in NuGet Packages Dialog, please make sure you include the Prerelease packages. The package is still in alpha stage.

Route Simulate Page

The package will create an area, under which all components and views are installed. Run your site, navigate to http://<SiteUrl>/rd (if you’re using IIS Express, it may look like http://localhost:52135/rd).

You will see this page:

Capture

Put the URL you want to test and click send.

Capture

The results is composed of 5 parts:

  1. The HTTP status code. In above example you see a 200, but if you hit a non-exist resource, you will see a 404.
  2. Route Data: what’s the route template and data parsed from URL.
  3. Route selecting: among all the routes, which one is chosen.
  4. Controller selecting: among all controllers found in assemblies, which one is selected.
  5. Action selecting: for the action on the selected controller, which action is selected and why. The chart visualize the decision making process.

Here’s another example:

GET http://localhost:57887/api/value

Capture

Since there is no “Value” controller in the system it hits a 404. You can see the “Controller” value in route data is “Value”. The route is selected. However there is no controller mapped.

In recent ASP.NET MVC update, we introduce ASP.NET MVC Facebook Template. In following article, I’m going to discuss one of the features come with the template.

What is Realtime Update?

Realtime update is a Facebook service provide you the capability to subscribe to the changes in the data. For example, you can listen to the changes of user’s Facebook likes. That expands your app’s capability.

How does Realtime Update work?

In short, Facebook requires your service to expose two endpoints. One accepts GET request, which is used to validate your service’s existence. And the other accepts POST request, which is receiving actually data regarding to changes.
The best way to learn Facebook Realtime Update is to read Facebook’s document: https://developers.facebook.com/docs/reference/api/realtime/

What’s in the box?

In template we already have a Realtime Update controller in place to help customer set up their Realtime Update endpoint easily. The controller UserRealtimeUpdateController derives from abstract controller FacebookRealtimeUpdateController. The latter is an abstract WebAPI controller ready to accept GET and POST request.

The GET request is taken care of. What you need to do is to set “Verify Token” to corresponding app setting web.config.

In terms of the POST request, you need to finish the HandleUpdateAsync method. The method is called whenever an update POST is sent from Facebook service.

A step by step guide

Following guides assume you already have a running Facebook App hosted publicly. Please note that you need to have your Realtime Update Endpoints exposed to public so that Facebook Realtime Service and reach to it.

Subscribe to changes

There are two ways to subscribe to a class of objects: you can do manually from App Settings or you can do programmatically by codes. Here let’s pick the easy one. Go to the Realtime Updates setting page under Facebook Apps Setting.

pic1
Add a subscription. You need to choose what object you want to observe (among the five options Facebook currently offer.) and the fields to listen to. You need to give a call back URL pointing to your Realtime Update endpoint. The endpoint accept both GET and POST request. Then the last but not least, you shall choose a “Verify Token”, which serves as the common secret for your App to identify the incoming request’s source.

pic2

Validate your endpoint

Now if you click “Test”, you’ll observe an error:

pic3

Why? Because Facebook send a GET request to your endpoint with a challenge value. It expects your endpoint to return the exact same value to prove its identity. (Here’s what the GET looks like http://fbrealtime.azurewebsites.net/api/realtime?hub.mode=subscribe&hub.challenge=742982751&hub.verify_token=sample_token)

So you need to implement your Realtime Update Endpoint now. If you create your project from Facebook template you will see following controller:

public class UserRealtimeUpdateController : FacebookRealtimeUpdateController
{
private readonly static string UserVerifyToken = ConfigurationManager.AppSettings["Facebook:VerifyToken:User"];

public override string VerifyToken
    {
        get
        {
return UserVerifyToken;
        }
    }

public override Task HandleUpdateAsync(ChangeNotification notification)
    {
        if (notification.Object == "user")
        {
foreach (var entry in notification.Entry)
            {
                // Your logic to handle the update here
            }
        }

throw new NotImplementedException();
    }
}

It is not completed. A few things to do.

    1. Remove the exception throwing, clean up the HandleUpdateAsync. Anything is better than exception.
    2. Go to your web.config. Set Verify Token to “Facebook:VerifyToken:User”.

Publish your site again. By the way, based on your WebAPI route, you probably want to double check your callback URL setting. Now go back to your Facebook App Settings and save the Realtime Update Subscription.

You done for the set up.

To expand your Realtime Update Controller you really just need exam the ChangeNotification objects, structure of which is mimic the JSON value from Facebook.

Potential difficulties

Realtime Update is hard to debug, mainly because it is passively invoked by Facebook not your codes. And if your dev box is not public to internet (for example behind a Firewall like most of the cooperation) you not able to debug your codes directly. As a result you have to rely on trace and logs to find out what’s happened.

Here’s some potential difficulties you need to deal with.

  1. The entire message loops includes a Facebook callback. It is very critical to make sure your service is reachable to Facebook. It is recommended to host your service in a public host and enable web API tracing for debugging.
  2. Don’t forget to set verify token.
  3. Your app shall have the permission to read the fields you subscribe to first. Otherwise, even service pass the GET challenge, it will never receive any POST update.
  4. Realtime Update is not very “Realtime”. Facebook queue the changes and send update a few minutes after changes are made. So be patient.

In the recent ASP.NET MVC refresh we introduced a new template — the Facebook Template. You can create Facebook App from it easily. I believe there are articles introducing the template, for example this post from Yao. In following post, I’d like to go through how to host a Facebook App in Windows Azure Web Site

I assume you’ve already read the tutorial and begin with build your App from the template, you shall eventually be able to run your App locally. You probably is still using IIS Express without SSL turning on. You’re using Local DB and the database is fulfilled with random data your generated during test.

Secure Canvas URL

There is a couple of things you may want to prepare locally.

First you need to ensure your application can run under SSL. Although Facebook allows you not to host your web site under SSL when it is in sandbox mode, you have to leave sandbox once your App is open to publish. As a result, Facebook will hit your secure canvas URL.

You can tried out SSL easily on your local machine by enable SSL.

Enable SSL

After that you should fill the SSL URL in Facebook App Setting

Update Secure Canvas URL

Now run your App. The iFrame will load page from HTTPS URL. If you’re using IE, you will see a warning. That is because IIS Express’s certificate is not trusted. I will discuss the topic in another post, but for now you can simple click “Show all content” to continue.

If your app works find at this point, your secure canvas URL preparation is done.

Upload your site to Azure Web Site

Azure Web Site was added in June 2012. For all of the options provided in Windows Azure to host web site, I like Azure Web Site the most. It is simple, light and powerful enough to deal with most of the cases.

Here’s a tutorial of how to create an Azure Web Site. Remember Facebook App is also an MVC web application. So instead of creating an Internet Application, you create a Facebook Application.

The rest of the part are largely the same. The tutorial publish the sites through Visual Studio Web Deploy and it works great. However, my favorite is publishing a website with Git.

Manage your App secret

Facebook App Secret is the common secret shared between your App and Facebook. You don’t want it to be compromised. But if that happens, you can reset the App Secret.

App Secret Reset

To update your secret to your App is extremely easy in Windows Azure Web Site. You don’t need to change web.config, you don’t need to redeploy. What your need to do is in the Azure Web Site’s CONFIG page

Config Tab

Find the app setting and update Facebook:AppSecret

Appsettings

In the following post, I’ll discuss a small sample show case how to broadcast WebAPI trace through SignalR.

  • SignalR is an async signaling library for .NET to help build real-time, multiuser interactive web applications. It can be used to push data from server to the client.
  • WebAPI tracing is the feature to track all internal activities in the server and log then to customized trace repository.

Scenario

User can connect to the server tracing page to get real-time trace updating. To access the information, they need to go through basic authentication.

Persisitent Connection

The core component is an persistent connection. Three actions are overrided:

  • When an connection is made, this connection is added to an unauthenticated group.
  • When an connection is released, it is removed from both authenticated and unauthenticated group. Just in case it is in either of them.
  • When a data is recieved from a connection, this data is expected to be a token string. The token string is used to authenticate the client. Details of the token will be discussed later.
public class TracePersistentConnection : PersistentConnection
{
    public static readonly string Authenticated = "authenticated";
    public static readonly string Unauthenticated = "unauthenticated";

    protected override Task OnConnectedAsync(IRequest request, string connectionId)
    {
        return Groups.Add(connectionId, Unauthenticated);
    }

protected override Task OnDisconnectAsync(string connectionId)
{
        return Groups.Remove(connectionId, Authenticated)
                     .ContinueWith(task => Groups.Remove(connectionId, Unauthenticated));
    }

    protected override Task OnReceivedAsync(IRequest request, string connectionId, string data)
    {
        if (ClientTokens.Instance.Exists(data))
        {
            return Groups.Add(connectionId, Authenticated)
                         .ContinueWith(task => Groups.Remove(connectionId, Unauthenticated));
        }
        else
        {
            return base.OnReceivedAsync(request, connectionId, data);
        }
    }
}

You must notice that there are not codes sending message to clients. That is because sending message is the responsibility of trace writer.

Trace Writer

A trace writer is replace the default implementation through WebAPI’s dependency resolver.

public class TraceWriter : ITraceWriter
{
    private ConcurrentQueue<TraceRecord> _traces;

    private TraceWriter()
    {
        _traces = new ConcurrentQueue<TraceRecord>();
    }

    private static TraceWriter s_instance = new TraceWriter();

    public static TraceWriter Instance
    {
        get { return s_instance; }
    }

    public void Trace(
        HttpRequestMessage request,
        string category,
        TraceLevel level,
        Action<TraceRecord> traceAction)
    {
        var trace = new TraceRecord(request, category, level);
        traceAction(trace);

        _traces.Enqueue(trace);

        var context =
                GlobalHost.ConnectionManager
                          .GetConnectionContext<TracePersistentConnection>();
        context.Groups.Send(
            TracePersistentConnection.Authenticated,
            new
            {
                trace.RequestId,
                trace.Request.RequestUri,
                Status = trace.Status.ToString(),
                Level = trace.Level.ToString(),
                trace.Message,
                trace.Category,
                TimeTicks = trace.Timestamp.Ticks,
                trace.Operator,
                trace.Operation,
                Exception = trace.Exception == null ? "" : trace.Exception.Message,
            });
    }
}

You may notice that when send the trace record to group, I construct a new object  rather than send out the trace it self and let the formatter take care of serialization. I actually tried to do so, but somehow SignalR hit some internal failure and that cause the message never make to the client. I guess it will be my next topic.

Server Setup

Everything is set up in global.asax.cs:

  • Add Route to the WebAPI controller
  • Add Route to the SignalR persistent connection
  • Replace trace writer in dependency resolver
  • Add authentication handler
    public class Global : System.Web.HttpApplication
    {
        protected void Application_Start(object sender, EventArgs e)
        {
            var config = GlobalConfiguration.Configuration;

            config.MessageHandlers.Add(new AuthenticationHandler());

            config.Services.Replace(typeof(ITraceWriter), TraceWriter.Instance);

            config.Routes.MapHttpRoute(
                "default",
                "api/{controller}/{action}",
                new { action = RouteParameter.Optional });

            RouteTable.Routes.MapConnection<TracePersistentConnection>("trace", "trace/{*operatopn}");
        }
    }

Client

The client is a simple HTML + Javascript page.

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <link rel="stylesheet" type="text/css" href="Style/Default.css" />
    <title>WebAPI Tracing + SignalR</title>
</head>
<body>
    <div id="auth">
        <h1>WebAPI Tracing + SignalR</h1>
        <table id="logTable">
            <tr>
                <td>Username</td>
                <td>
                    <input type="text" id="txtUsername" value="admin" /></td>
            </tr>
            <tr>
                <td>Password</td>
                <td>
                    <input type="password" id="txtPassword" value="password" /></td>
            </tr>
        </table>
        <input type="button" id="btnLogin" value="Log in" />
        <input type="button" id="btnLogout" value="Log out" />
    </div>

    <div id="trace">
        <h1>Trace Records</h1>

        <input type="button" id="btnClear" class="button" value="Clear" />

        <div id="message" class="output"></div>
        <div id="exception" class="output"></div>

        <table id="tracetable">
            <tr id="header">
                <th id="requrl">URL</th>
                <th id="level">Level</th>
                <th id="category">Category</th>
                <th id="operator">Operator</th>
                <th id="operation">Operation</th>
                <th id="status">Status</th>
            </tr>
        </table>
    </div>

    <script type="text/javascript" src="Scripts/jquery-1.6.4.js"></script>
    <script type="text/javascript" src="Scripts/jquery-ui-1.8.20.js"></script>
    <script type="text/javascript" src="Scripts/jquery.base64.js"></script>
    <script type="text/javascript" src="Scripts/jquery.signalR-0.5.2.js"></script>
    <script type="text/javascript" src="Scripts/Home.js"></script>
</body>
</html>

 


$(function () {
    function updateTraceTable(trace) {
        var row = $('<tr></tr>');
        row.append('<td>' + trace.RequestUri + '</td>');
        row.append('<td>' + trace.Level + '</td>');
        row.append('<td>' + trace.Category + '</td>');
        row.append('<td>' + trace.Operator + '</td>');
        row.append('<td>' + trace.Operation + '</td>');
        $('<td>' + trace.Status + '</td>').appendTo(row);

        row.insertAfter('#header');

        row.hover(
            function () {
                $('#message').text(trace.Message);
                $('#exception').text(trace.Exception);
                $(this).css("background-color", "lightgreen");
            },
            function () {
                $('#message').text("");
                $('#exception').text("");
                $(this).css("background-color", "white");
            });
    };

    function setupTraceConnection(token) {
        var connection = $.connection('/trace');

        connection.received(function (trace) {
            $('#debuginfo').text(trace);
            updateTraceTable(trace);
        });

        connection.start().done(
            function () {
                connection.send(token);
            });

        $('#btnClear').click(function () {
            $('#debuginfo').text("clear the trace records");
            $('td', '#tracetable').remove();
        });
    };

    $('#btnLogout').hide();
    $('div#trace').hide();

    $('#btnLogin').click(function (event) {
        var username = $('input#txtUsername').val();
        var password = $('input#txtPassword').val();

        var options = {
            url: 'api/TraceAuthentication/GetToken',
            type: 'GET',
            data: { 'username': username },
            beforeSend: function (xhr) {
                var raw = username + ":" + password;
                var encoded = jQuery.base64.encode(raw);
                xhr.setRequestHeader("Authorization", "Base " + encoded);
            },
            timeout: '60000',
            success: function (token) {
                $('div#trace').show();
                $('#logTable').hide();
                $('#btnLogin').hide();
                $('#btnLogout').show();
                setupTraceConnection(token);
            },
            error: function (data, status, err) {
                alert(err);
            }
        }
        $.ajax(options);
    });

});

Above are the html and the javascript used to update it. Line 26 to 33 of javascript code snippet actually set up the connection to persistent connection. You may wonder what’s the magic string “/trace”. It is the route set up to the PersistentConnection defined in global.asax.cs.

Till now the server is fully functional except one part: security. You don’t want users to see the trace just because they ‘accidentally’ know the endpoint. So here’s the last piece.

Authentication

Here’s the logic I adopted in this sample:

The client first visit a WebAPI controller to get a token. The controller is protected by basic authentication (or any security model you can image). The token is a security string combing client information and a dynamic server string. The server register the token to a global bag. The client uses the token to send out first message to Hub/PersisitentConnection after connected, which will make itself into an authenticated group, to which Trace information are broadcasted to.

Some of the logic have been already presented in above code snippets.

  • In the TracePersistentConnection, for a new created connection a token is returned.
  • In the TracePersistentConnection, it actively manages the groups.
  • In the client javascript, right after the connection is setup a token is sent to server to announce client’s identity.
  • In the trace writer, the information only writes to the authenticated group.

What missing is a global structure store the tokens.


    public class ClientTokens
    {
        private static ClientTokens s_instnace = new ClientTokens();
        private ConcurrentBag<string> _tokens = new ConcurrentBag<string>();

        private ClientTokens()
        {
        }

        public static ClientTokens Instance
        {
            get
            {
                return s_instnace;
            }
        }

        public bool Exists(string token)
        {
            return _tokens.Contains(token);
        }

        public void AddToken(string token)
        {
            _tokens.Add(token);
        }
    }

Summary

I tried to demo following features in this sample:

  • WebAPI and SignalR side by side
  • Broadcast trace in real-time
  • Authentication pattern

Some thing worth to dig deeper:

  • Serialization in SignalR
  • More authentication pattern

Link

There aren’t lots of batching scenario samples out there. The one stands out among them is Brad Wilson’s Batching Handler for ASP.NET Web API. The sample is concise and elegant by leveraging route-specific endpoint handler as well as await async programming model in .net 4.5.

However there is one drawback when you try to customize you own scenario base on this sample, that is if a MessageHandler is added to the configuration, then all the embed request will return a 500 Internal Server Error.

Analysis

Exception is thrown from HttpClientFactory.CreatePipeline, when it detects that the first MessageHandler (actually all of them) already has InnerHandler property set. What cause the failure is because the HttpConfiguration is reused in an in-memory HttpServer created by BatchHandler. When the in-memory HttpServer is first initialized it tries to re-create the pipeline.

Please note that this issue is tracked by Issue 260 as a known bug. Once it is fixed then you don’t need following mitigation any more.

Mitigation

Web-host

Here’s my amended sample: https://github.com/troydai/webapi.sample.batching/tree/master/webapi.sample.batching

The core solution is to rewrite the Initialize method by deriving a new class from HttpServer.

    public class BatchServer : HttpServer
    {
        private readonly HttpConfiguration _config;

        public BatchServer(HttpConfiguration configuration)
            : base(configuration)
        {
            _config = configuration;
        }

        protected override void Initialize()
        {
            var firstInPipeline = _config.MessageHandlers.FirstOrDefault();
            if (firstInPipeline != null && firstInPipeline.InnerHandler != null)
            {
                InnerHandler = firstInPipeline;
            }
            else
            {
                base.Initialize();
            }
        }
    }

When the BatchingServer is initialized, it detects if the pipeline already exists by looking for first handler and it’s inner handler. Otherwise it call to the base implementation. This log avoid the re-creation issue. The BatchingServer replace  the HttpServer in BatchHandler

Self-host

In the same project, I created another self-host sample. The initial intention is to use it as a test bed to rapidly verify ideas, however it turns out that under self-host the solution could be very different.

    public class BatchHandler : DelegatingHandler
    {
        private HttpMessageInvoker _server;

        public void SetHandler(HttpMessageHandler handler)
        {
            _server = new HttpMessageInvoker(handler);
        }

        // remaining are the sample implementation
    }

Rather than a configuration, the BatchHandler accepts a MessageHandler. The actual anticipation is an HttpServer.

var config = new HttpSelfHostConfiguration(baseAddress);
config.MessageHandlers.Add(new MarkingMessageHandler());

var batchHandler = new BatchHandler();

config.Routes.MapHttpRoute(
    "batch",
    "api/batch",
    null,
    null,
    batchHandle);

config.Routes.MapHttpRoute(
    "default",
    "api/{controller}/{id}",
    new { id = RouteParameter.Optional });

this.Server = new HttpSelfHostServer(config);

batchHandler.SetHandler(this.Server);

Since user can access to the HttpServer instance, which enables a much simpler solution that is resubmit the request to the HttpServer.

Alternative Mitigation

There is other mitigations which doesn’t involve changes on HttpConfiguration.

  • If submitting the embedded request to the original pipeline is not required, then HttpServer is not required. What you can do is push the embedded request to the separated handler pipeline, which is specifically for embedded requests, and ultimately to the dispatcher. In this solution one particularly important thing is you have to manually add route information to every embedded requests, because these request are created from multi-part content and haven’t gone through HttpServer. They’re so “innocent” that if you don’t so, they will “lost their way” and return 404.
  • Or you can clone the HttpConfiguration, and feed it to a new created HttpServer. To do so, you shall clone all the MessageHandler as well, make sure the InnerHandler are cleaned. This mitigation does not work well if the MessageHandler is not clonable, for example a Singleton-pattern MessageHandler.

Summary

  • The root cause of the issue is that HttpServer tries to re-create pipeline based on a reused HttpConfiguration.
  • This issue is known and tracked by Issue 260.
  • Sampling two mitigation solutions under web-host and self-host.

Links

Follow

Get every new post delivered to your Inbox.