Fixing Windows Phone web request caching on the server side
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:
And here’s what happens when she clicks the button the second time:
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:
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:
- You can force your app to make a fresh request every time (set max-age to 0).
- 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
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();