Prevent IE11 caching GET call in Angular 2

I have a rest endpoint that returns a list on a GET call. I also have a POST endpoint to add new items and a DELETE to remove them. This works in Firefox and Chrome, and the POST and DELETE work in IE11. However, the GET in IE11 only works on initial load of the page. Refreshing returns cached data. I have seen post about this behavior in Angular 1 but nothing for Angular 2(release candidate 1).

52942 次浏览

Forward the stackoverflow response Angular IE Caching issue for $http, you should add the headers 'Pragma', 'no-cache', 'If-Modified-Since' to each 'GET' request.

The interceptor's scenario is not supported to angular 2 anymore. So you should extend the http as it is described here What is httpinterceptor equivalent in angular2?.

Angular 4.3 now includes the HttpClient service, which supports interceptors.

A little late, but I ran into the same problem. For Angular 4.X I wrote a custom Http class to append a random number to the end to prevent caching by IE. It is based on the 2nd link by dimeros (What is httpinterceptor equivalent in angular2?). Warning: not guaranteed to be 100% bug free.

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Http, Response, XHRBackend, RequestOptions, RequestOptionsArgs,
URLSearchParams } from '@angular/http';


@Injectable()
export class NoCacheHttp extends Http {
constructor(backend: XHRBackend, options: RequestOptions) {
super(backend, options);
}


get(url: string, options?: RequestOptionsArgs): Observable<Response> {
//make options object if none.
if (!options) {
options = { params: new URLSearchParams() };
}
//for each possible params type, append a random number to query to force no browser caching.
//if string
if (typeof options.params === 'string') {
let params = new URLSearchParams(options.params);
params.set("k", new Date().getTime().toString());
options.params = params;


//if URLSearchParams
} else if (options.params instanceof URLSearchParams) {
let params = <URLSearchParams>options.params;
params.set("k", new Date().getTime().toString());


//if plain object.
} else {
let params = options.params;
params["k"] = new Date().getTime().toString();
}
return super.get(url, options);
}
}

For Angular 2 and newer, the easiest way to add no-cache headers by overriding RequestOptions:

import { Injectable } from '@angular/core';
import { BaseRequestOptions, Headers } from '@angular/http';


@Injectable()
export class CustomRequestOptions extends BaseRequestOptions {
headers = new Headers({
'Cache-Control': 'no-cache',
'Pragma': 'no-cache',
'Expires': 'Sat, 01 Jan 2000 00:00:00 GMT'
});
}

Module:

@NgModule({
...
providers: [
...
{ provide: RequestOptions, useClass: CustomRequestOptions }
]
})

Today, I also had this problem, (damn IE). In my project, I use httpclient, that hasn't BaseRequestOptions. We should use Http_Interceptor to resolve it!

import { HttpHandler,
HttpProgressEvent,
HttpInterceptor,
HttpSentEvent,
HttpHeaderResponse,
HttpUserEvent,
HttpRequest,
HttpResponse } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';


export class CustomHttpInterceptorService implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler):
Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> {
const nextReq = req.clone({
headers: req.headers.set('Cache-Control', 'no-cache')
.set('Pragma', 'no-cache')
.set('Expires', 'Sat, 01 Jan 2000 00:00:00 GMT')
.set('If-Modified-Since', '0')
});


return next.handle(nextReq);
}
}

Module provide

@NgModule({
...
providers: [
...
{ provide: HTTP_INTERCEPTORS, useClass: CustomHttpInterceptorService, multi: true }
]
})

Disable browser caching with meta HTML tags:-

<meta http-equiv="cache-control" content="no-cache, must-revalidate, post-check=0, pre-check=0">
<meta http-equiv="expires" content="0">
<meta http-equiv="pragma" content="no-cache">

Edit: See comment below - this is not necessary (in the vast majority of cases).

Expanding on Jimmy Ho's answer above, I only want to prevent caching of my API calls, and not other static content which will benefit from being cached. All of my API calls are to URLs that contain "/api/", so I amended Jimmy Ho's code with a check that only adds the cache headers if the requested URL contains "/api/":

import { HttpHandler,
HttpProgressEvent,
HttpInterceptor,
HttpSentEvent,
HttpHeaderResponse,
HttpUserEvent,
HttpRequest,
HttpResponse } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';


export class CustomHttpInterceptorService implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler):
Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> {
// Only turn off caching for API calls to the server.
if (req.url.indexOf('/api/') >= 0) {
const nextReq = req.clone({
headers: req.headers.set('Cache-Control', 'no-cache')
.set('Pragma', 'no-cache')
.set('Expires', 'Sat, 01 Jan 2000 00:00:00 GMT')
.set('If-Modified-Since', '0')
});


return next.handle(nextReq);
} else {
// Pass the request through unchanged.
return next.handle(req);
}
}

}

As answered above, you can use http request interceptor to modify or set a new header on the request. Below is a much simpler way of setting headers on http request interceptor for Later angular versions(Angular 4+). This approach would only set or update a certain request header. This is to avoid removing or overriding some important headers like the authorization header.

// cache-interceptor.service.ts
import { Injectable } from '@angular/core';
import {
HttpInterceptor,
HttpRequest,
HttpHandler,
} from '@angular/common/http';


@Injectable()
export class CacheInterceptor implements HttpInterceptor {


intercept(req: HttpRequest<any>, next: HttpHandler) {
const httpRequest = req.clone({
headers: req.headers
.set('Cache-Control', 'no-cache')
.set('Pragma', 'no-cache')
.set('Expires', 'Sat, 01 Jan 2000 00:00:00 GMT')
})


return next.handle(httpRequest)
}
}


// app.module.ts


import { HTTP_INTERCEPTORS } from '@angular/common/http'
import { CacheInterceptor } from './cache-interceptor.service';


// on providers
providers: [{ provide: HTTP_INTERCEPTORS, useClass: CacheInterceptor, multi: true }]