60

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
        });
    }
3
  • 2
    I have added No-Cache headers on the server side and it worked for now. Commented Jun 6, 2013 at 20:50
  • 1
    Ufuk, that seems to be the most effective way to do take care of the problem "globally" or in a more scalable way, without having to make every ajax/REST call unique. You should answer the question, so I can mark it as the answer! You deserve the credit. Commented Jun 7, 2013 at 20:02
  • BTW, I am using node/express for the web server, and I accomplished what Ufuk recommended by adding the following code to express config: 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(); }) Commented Jun 7, 2013 at 20:08

10 Answers 10

50

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';
}]);
Sign up to request clarification or add additional context in comments.

3 Comments

JSHint might complain about $httpProvider.defaults.headers.get['Pragma'] = 'no-cache'; so it's safe to change that line to $httpProvider.defaults.headers.get.Pragma = 'no-cache';
Can I don't use those extras?
For those of you who are using Angular Resource you have to substitute $httpProvider with $resourceProvider
36

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
}

2 Comments

is there a DRY implementation of this?
@meffect Adding it as a global filter maybe?
33

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 }
    );
}

3 Comments

This solved our issue. We also had to add if(actionExecutedContext.Request.Method.Method == "GET") in the OnActionExecuted event to make it only fire on GET requests
You'll have to compare to the HttpMethod and not just the string. Like this: actionExecutedContext.Request.Method == HttpMethod.Get
I had been using the method suggested by @cnmuc, but it would interfere with some External rest services I use (some services rejected the request stating that those headers were not accepted). Applying your filter worked like a charm.
32

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();
});

2 Comments

Does this also disable caching on static files? i.e. your "public" folder?
@Tony, just add that middleware after serving the static files
11

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;
               }
           };
    });

1 Comment

A nicer solution instead of manipulate the url would be: check if the request has parameters config.params = config.params || [] and then add your default noCache parameter config.params['noCache'] = new Date().getTime();
4

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!!!');
});

1 Comment

God bless StackOverFlow!
4

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;
}

Comments

3

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.

Comments

2

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');

1 Comment

Do you mean Cache-Control: no-cache?
-3

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.