0 Comments

Windows Phone caches web requests aggressively. We’ve all seen a case where the first request to the web server works as expected but then the following requests fail to return up-to-date data. Instead of just adding the timestamp to the request URL, you can fix this issue on the server side.

Background

Here’s a simple app next to Fiddler which demonstrates the problem. The app issues a web request with every button click:

        private async void DoRequest_OnClick(object sender, RoutedEventArgs e)
        {
            var client = new WebClient();
            await client.DownloadStringTaskAsync("http://adafy.com");

            requestCount += 1;

            this.RequestCount.Text = requestCount.ToString();
        }

First, a screenshots where user has clicked the button the first time:

image

And here’s what happens when she clicks the button the second time:

image

As you can see from the Fiddler, no request is sent to the server: Windows Phone returns the web request’s response from the cache.

The most often seen solution to this is to add a timestamp to the URL, this way bypassing the Windows Phone’s caching mechanisms. Example:

image

The requested URL changes every time, this way bypassing Windows Phone’s caching.

Solution

All this is because of the web request’s response headers.Make the server return the following Cache-Control header and your app will work exactly as you want:

Cache-Control: public, max-age=15, must-revalidate

Where max-age defines how long (in seconds) your Windows Phone app will cache the request.

By changing the headers you can configure how the app behaves. For example:

  1. You can force your app to make a fresh request every time (set max-age to 0).
  2. You can set exact caching (in seconds) for the requests.

Setting the headers on ASP.NET MVC

ASP.NET MVC offers multiple ways of configuring cache-control header. For example you can set the header on every action:

Response.Headers.Set("Cache-Control", "public, max-age=15, must-revalidate");

Or you can create a filter which is automatically executed after each request. The following StackOverflow questions show multiple solutions:

http://stackoverflow.com/questions/7087859/how-do-i-add-site-wide-no-cache-headers-to-an-mvc-3-app

http://stackoverflow.com/questions/9234044/asp-net-mvc-and-ie-caching-manipulating-response-headers-ineffective

Setting the headers on Azure Blob Storage

If you use Azure Blob Storage as your app’s backend, you can set caching for each blob using the blob.SetProperties-method:

            var blob = container.GetBlockBlobReference(file);
            blob.UploadText(content);

            blob.Properties.CacheControl = string.Format("public, max-age={0}, must-revalidate", (int)cacheAge.TotalSeconds);
            blob.Properties.ContentType = contentType;

            blob.SetProperties();