Better Way to Prevent IE Cache in AngularJS?

The question:

I currently use service/$resource to make ajax calls (GET in this case), and IE caches the calls so that fresh data cannot be retrieved from the server. I have used a technique I found by googling to create a random number and append it to the request, so that IE will not go to cache for the data.

Is there a better way than adding the cacheKill to every request?

factory code

.factory('UserDeviceService', function ($resource) {

        return $resource('/users/:dest', {}, {
            query: {method: 'GET', params: {dest: "getDevicesByUserID"}, isArray: true }
        });

Call from the controller

$scope.getUserDevices = function () {
        UserDeviceService.query({cacheKill: new Date().getTime()},function (data) {
            //logic
        });
    }

The Solutions:

Below are the methods you can try. The first solution is probably the best. Try others if the first one doesn’t work. Senior developers aren’t just copying/pasting – they read the methods carefully & apply them wisely to each case.

Method 1

As described in one of my other posts, you could disable caching globally in the $httpProvider:

myModule.config(['$httpProvider', function($httpProvider) {
    //initialize get if not there
    if (!$httpProvider.defaults.headers.get) {
        $httpProvider.defaults.headers.get = {};    
    }    

    // Answer edited to include suggestions from comments
    // because previous version of code introduced browser-related errors

    //disable IE ajax request caching
    $httpProvider.defaults.headers.get['If-Modified-Since'] = 'Mon, 26 Jul 1997 05:00:00 GMT';
    // extra
    $httpProvider.defaults.headers.get['Cache-Control'] = 'no-cache';
    $httpProvider.defaults.headers.get['Pragma'] = 'no-cache';
}]);

Method 2

As binarygiant requested I am posting my comment as an answer. I have solved this problem by adding No-Cache headers to the response on server side. Note that you have to do this for GET requests only, other requests seems to work fine.

binarygiant posted how you can do this on node/express. You can do it in ASP.NET MVC like this:

[OutputCache(NoStore = true, Duration = 0, VaryByParam = "None")]
public ActionResult Get()
{
    // return your response
}

Method 3

For those using ASP.NET Web API 2 the equivalent solution would be this (Web API does not use same caching logic as MVC):

public class NoCacheHeaderFilter : ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        if (actionExecutedContext.Response != null) // can be null when exception happens
        {
            actionExecutedContext.Response.Headers.CacheControl =
                new CacheControlHeaderValue { NoCache = true, NoStore = true, MustRevalidate = true };
            actionExecutedContext.Response.Headers.Pragma.Add(new NameValueHeaderValue("no-cache"));

            if (actionExecutedContext.Response.Content != null) // can be null (for example HTTP 400)
            {
                actionExecutedContext.Response.Content.Headers.Expires = DateTimeOffset.UtcNow;
            }
         }
    }
}

then attach it in WebApiConfig.cs:

public static void Register(HttpConfiguration config)
{
    ....
    config.Filters.Add(new NoCacheHeaderFilter());

    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );
}

Method 4

Enabling noCache in the is instance was the best way to accomplish this:

In node/express this works to prevent IE from caching those requests:

app.use(function noCache(req, res, next) {
    res.header("Cache-Control", "no-cache, no-store, must-revalidate");
    res.header("Pragma", "no-cache");
    res.header("Expires", 0);
    next();
});

Method 5

you may add an interceptor to generate unique request url. Also you may remove console.log calls

myModule.config(['$httpProvider', function($httpProvider) {
 $httpProvider.interceptors.push('noCacheInterceptor');
}]).factory('noCacheInterceptor', function () {
            return {
                request: function (config) {
                    console.log(config.method);
                    console.log(config.url);
                    if(config.method=='GET'){
                        var separator = config.url.indexOf('?') === -1 ? '?' : '&';
                        config.url = config.url+separator+'noCache=' + new Date().getTime();
                    }
                    console.log(config.method);
                    console.log(config.url);
                    return config;
               }
           };
    });

Method 6

I get it resolved by:

$http.get("/your_url?rnd="+new Date().getTime()).success(function(data, status, headers, config) {
    console.log('your get response is new!!!');
});

Method 7

Koajs equivalent of binarygiant’s answer:

app.use(route.get('*', noCache));

function* noCache(path, next){
    this.set('cache-control', 'no-cache, no-store, must-revalidate');
    this.set('pragma',  'no-cache');
    this.set('expires', 0);
    yield next;
}

Method 8

While this approach:

myModule.config(['$httpProvider', function($httpProvider) {
    //initialize get if not there
    if (!$httpProvider.defaults.headers.get) {
        $httpProvider.defaults.headers.get = {};    
    }
    //disable IE ajax request caching
    $httpProvider.defaults.headers.get['If-Modified-Since'] = '0';
}]);

Is correct, ‘0’ is not a valid value for the If-Modified-Since header. It needs to be a valid HTTP-date, for example:

If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT

According to the spec:

A recipient MUST ignore the If-Modified-Since header field if the
received field-value is not a valid HTTP-date, or if the request
method is neither GET nor HEAD.

So better be safe than sorry and use an actual date in the past.

If you have any control over the server output, it would be preferable to add no caching headers to that instead.

Method 9

My solution was adding Cache-Control: no-cache header on the server, plus adding $templateCache.remove() before changing state. I’m using angular-ui/ui-router. I was having issue with IE11 and Edge browser.

$templateCache.remove('/partials/details.html');
$state.go('details');

Method 10

An obvious solution is to use unique urls. But how can the router urls be changed post initialization
Disabling browser caches is not an option, since we need this for normal operations.
You could remove templates from the $templateCache when those are no
longer needed.
(http://docs.angularjs.org/api/ng.$templateCache).
Those new ones are added to the cache as soon as downloading completes.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Comment