防止IE11在Angular 2中缓存GET调用


74

我有一个休息端点,可以在GET调用中返回列表。我也有一个POST端点来添加新项目,而DELETE来删除它们。这在Firefox和Chrome中有效,而POST和DELETE在IE11中有效。但是,IE11中的GET仅在页面的初始加载时有效。刷新将返回缓存的数据。我已经在Angular 1中看到有关此行为的帖子,但对于Angular 2(发布候选版本1)则一无所获。


4
如果您的GET api没有指定Any Cache Control头->这意味着如果状态为200 OK,则响应是可缓存的。
Loc

3
另请参见stackoverflow.com/questions/36500804/…,了解客户端的解决方法。
君特Zöchbauer

@Loc我添加了Cache-Control值“ no-store”和“ no-cache”,并且仍然仅在IE中获得相同的结果。
cmaynard


4
看起来我需要指定更多的缓存头,我不得不Cache-Control: not-store, no-cache添加buPragma: no-cache Expires: 0
cmaynard

Answers:


48

今天,我也遇到了这个问题,(该死的IE)。在我的项目中,我httpclient没有使用BaseRequestOptions。我们应该用Http_Interceptor它来解决!

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

模块提供

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

3
谢谢,这是Angular 5的正确答案。但是,由于我在原始问题中指定了Angular 2,因此我将该答案标记为对这个问题正确,请投票!
cmaynard '18

2
如果使用CORS,则可能需要将所有标头(Cache-Control,Pragma等)添加到Access-Control-Request-Headers服务器端。否则,您会遇到CORS问题。这意味着您的请求将失败。
Arikael

57

对于Angular 2和更高版本,通过重写添加无缓存标头的最简单方法是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'
    });
}

模块:

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

3
对于使角度请求中的所有请求都没有缓存,这是一个很好的解决方案,但是我不希望所有请求都具有这种行为,因为某些请求可以很好地缓存。我去设置适当的头服务器端。我宁愿服务器仍然具有缓存功能。我可能措辞不好。
cmaynard '17

1
@cmaynard我在寻找如何为Andular设置全局缓存时遇到了您的问题,因此从Google角度来看,您的措辞非常适合人们搜索的内容:)
Vitaliy Ulantikov

该解决方案并没有为我工作,我不得不手动禁用缓存使用docs.netapp.com/sgws-110/...
Jaikumar ^ h Manjunath

1
是的,这很感谢。IE11的另一个刺激!
Andrew Junior Howard

@cmaynard我正在考虑这个问题,也许并且选择是仅将此httpinterceptor提供给您的Rest服务。
乔伊·高夫


4

编辑:请参阅下面的评论-这是没有必要的(在大多数情况下)。

在以上Jimmy Ho的回答的基础上,我只想防止缓存我的API调用,而不是希望从缓存中受益的其他静态内容。我所有的API调用都是针对包含“ / api /”的URL,因此,我对Jimmy Ho的代码进行了修改,使其仅在所请求的URL包含“ / 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);
    }
}

}


2
HttpInterceptor仅影响通过角度的请求(因此通常在您的角度服务中使用HttpClient),API请求有99%的时间通过该请求。因此,我认为没有必要进行此额外检查。除非出于某种原因在服务层内请求静态内容,否则不可以。
LambdaCruiser

@LambdaCruiser,是的,您说的很对。并不是必须的。我将把它留在这里,以防万一有人陷入同一个陷阱。
AndrWeisR

1

如上所述,您可以使用http请求拦截器修改或设置请求的新标头。下面是在HTTP请求拦截器上为以后的Angular版本(Angular 4+)设置标头的简单得多的方法。这种方法只会设置或更新某个请求标头。这是为了避免删除或覆盖一些重要的标头,例如授权标头。

// 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 }]

0

禁用带有元HTML标签的浏览器缓存:-

<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">

-1

有点晚了,但是我遇到了同样的问题。对于Angular 4.X, 我编写了一个自定义Http类,将一个随机数附加到末尾以防止IE进行缓存。它基于dimeros的第二个链接(angular2中的httpinterceptor等效项是什么?)。警告:不能保证是100%无错误。

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

1
我过去曾使用过这种技术来“愚弄”缓存。我认为这很有用,但通常最好设置适当的标头。
cmaynard '17
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.