try-catch-FAIL

Failure is inevitable

NAVIGATION - SEARCH

Simulate Slow Web API Requests Using an Action Filter in ASP.NET Web API

How much time do you spend on your SPA application's "loading" screens? What about their "failed to load" screens? These things are important in a normal SPA, but they're even more important in a mobile app built with PhoneGap or Cordova. Yet these important UX issues are easily overlooked as we build our applications.

With almost no effort, you can easily see how your application will behave on slower, real-world connections, and how your application will behave when it fails to communicate with your server.

"So... what's the app doing?"

I spend a good deal of my time building mobile apps. When I build and test things on my local machine, everything loads fast. There's no network latency, so it's easy to forget what life is going to be like for real users of my apps.

In the real world, users often won't have WiFi. Around here in Tennnessee, they might not have 4G. Heck, they might not even have 3G. Or they may have no connection at all. So their experience isn't going to be the peppy, quick load times we see during development. How is the app going to look and feel for them?

The truth is, most of the time, I don't give that the attention it deserves, especially when I'm building mobile applications. I bet many of you neglect that aspect, too.

Apps should clearly communicate their state. If they're loading, users should be able to tell that. If the user invokes an action, the UI should let them know the action is being performed, and it should prevent them from doing something that might be a Bad Thing, like resubmitting the same action. If the action fails, the app should let them retry it without losing their work.

And for mobile apps that make use of remote data or resources, apps should elegantly handle the case of the resource being unavailable, or the case of an API call timing out. Those things will happen to your users!

Simulating Poor Connections Using Chrome

While it's all too easy to overlook this pain during our day-to-day development, there are ways to force some of that pain upon ourselves. Chrome actually has the ability to simulate slow network connections built right into it. I haven't looked, but I bet Firefox has similar capabilities, too.

From the Developer Tools, you can open the Network Tab, then choose from some predefined throttling profiles. You can also make your own profile.


You can find out more about these network tools in the official docs.

This is an easy way to test things out, but it isn't perfect. It's tough to simulate packet loss or an inconsistent connection. It also only works if you are actually using Chrome. If you are testing your app on a mobile device, then obviously the tools in Chrome aren't going to be of much use, either.

That's why I prefer a different, code-based approach.

Simulating Connection Problems During Development with Action Filters

If we don't want to use Chrome's built-in tooling, another quick-and-easy option is to apply action filters that create the same symptoms that users will run into when using one of our apps: slow load times, calls failing, etc.

Let's start with an action filter to add some latency to our calls...

The LagResponse Action Filter

Sometimes, API calls in production will take a bit to return. It could be due to load on your app, or it could be due to general network latency. Whatever the case, your users are likely going to encounter it. You can force this experience in your local development environment by applying this simple little filter:

public class LagResponseAttribute : ActionFilterAttribute
{
    private static readonly Random Rand = new Random();

    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        Thread.Sleep(TimeSpan.FromSeconds(Rand.Next(3, 7)));
        base.OnActionExecuted(actionExecutedContext);
    }
}

The filter runs after the request has been processed (meaning after our controller has executed and generated a result). All that happens is that a random "Sleep" is performed, adding a delay of between 3 and 7 seconds to the request. I've found that to be long enough to give me a feel for the "waiting" UX in my app, but you should adjust that range to match whatever feels "right" to you.

Let's take a look at a slightly more difficult situation to fake: losing connection to the API altogether!

The TerminateRequest Filter

This was a fun one to write. What we'd like to happen is for the client code to make a request, then to have the request completely dropped. We don't want it to receive a 400 or 500 error, we want it to get nothing back. That's a little harder to do with ASP.NET Web API.

I tried several things. I could interrupt the request, but the client would still get back an HTTP response status code, which is not what's going to happen to end-users when their device's network connection is dropping out.

Here's the solution I came up with. Note that it will only work with IIS hosting (and Azure Web Apps). It won't work in a self-hosted app, because this solution uses HttpContext.

public class TerminateRequestAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        //Pretend we're having trouble connecting...
        Thread.Sleep(5000);

        //And now act like we can't connect at all. 
        HttpContext.Current.Response.Clear();
        HttpContext.Current.Response.Close();

        throw new InvalidOperationException("TerminateRequestAttribute applied!");
    }
}

Note that this action filter runs before the request is actually processed. That is important. We want to pretend that the request never actually made it to the server.

Rapid Feedback is Important!

These filters are handy for seeing how your application will behave in poor-connectivity scenarios, but you obviously don't want to use them all the time. Rapid feedback is important in development. You don't want to interrupt that flow, so I wouldn't use these all the time. Instead, I recommend using this as you are working to polish up a new "screen" or feature. That's typically what I do.

And it should obviously go without saying, buuuut....

Don't use this in production!

Duh. 😃

created with the free version of Markdown Monster

About Matt Honeycutt...

Matt Honeycutt is a software architect specializing in ASP.NET web applications, particularly ASP.NET MVC. He has over a decade of experience in building (and testing!) web applications. He’s an avid practitioner of Test-Driven Development, creating both the SpecsFor and SpecsFor.Mvc frameworks.

He's also an author for Pluralsight, where he publishes courses on everything from web applications to testing!

blog comments powered by Disqus