Better Way to Prevent IE Cache in AngularJS?
CachingAngularjsCaching Problem Overview
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
});
}
Caching Solutions
Solution 1 - Caching
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';
}]);
Solution 2 - Caching
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
}
Solution 3 - Caching
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();
});
Solution 4 - Caching
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 }
);
}
Solution 5 - Caching
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;
}
};
});
Solution 6 - Caching
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!!!');
});
Solution 7 - Caching
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;
}
Solution 8 - Caching
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.
Solution 9 - Caching
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');
Solution 10 - Caching
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.