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

78

我有一个REST端点,在GET调用时返回一个列表,还有一个POST端点用于添加新项目和一个DELETE端点用于删除它们。这在Firefox和Chrome中起作用,在IE11中POST和DELETE也能够使用。然而,在IE11中,仅在页面初始加载时GET才能正常工作。刷新会返回缓存数据。我看过关于在Angular 1中出现此行为的帖子,但对于Angular 2(发布候选版1)没有找到相关内容。


4
如果您的GET API未指定任何缓存控制头,则意味着如果状态为200 OK,则响应是可缓存的。 - LHA
3
请参考此处 https://dev59.com/sVoV5IYBdhLWcg3wPswY,了解针对客户端的解决方案。 - Günter Zöchbauer
@Loc 我添加了Cache-Control的值no-store和no-cache,但在IE中仍然得到相同的结果。 - cmaynard
1
https://dev59.com/vlwY5IYBdhLWcg3wwp84 - Günter Zöchbauer
4
看起来我需要指定更多的缓存头,我原本有 Cache-Control: not-store, no-cache 但需要加上 Pragma: no-cacheExpires: 0 - cmaynard
不要忘记,你的后端应该配置成接受这些头部信息。 - Sargon
7个回答

58

对于Angular 2及以上版本,覆盖RequestOptions是添加 no-cache headers 最简单的方法:

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
这是一个很好的解决方案,可以使来自Angular请求的所有请求均不缓存,但是我不希望所有请求都具有这种行为,因为某些请求可以很好地进行缓存。我选择在服务器端设置适当的标头。无论如何,我更愿意让服务器拥有智能缓存控制。也许我的问题表述不太清楚。 - cmaynard
1
@cmaynard,我在寻找如何为Angular设置全局缓存时遇到了您的问题,所以从谷歌的角度来看,您的措辞对于人们搜索的内容是完美的 :) - Vitaliy Ulantikov
这个解决方案对我没有起作用,我不得不通过使用http://docs.netapp.com/sgws-110/index.jsp?topic=%2Fcom.netapp.doc.sg-app-install%2FGUID-1E34D79A-2EB8-4A0D-AA47-E7BB62B8C77B.html手动禁用缓存。 - resolve_promise
1
是的,这个有效了,谢谢。又是IE11的另一个烦恼!! - Andrew Howard
@cmaynard 我在考虑这个问题,也许一个选项是只为您的Rest服务提供此http拦截器。 - Joey Gough

52

今天我也遇到了这个问题(该死的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 }
    ]
})

4
谢谢,这是 Angular 5 的正确答案。然而,由于我在原始问题中指定了 Angular 2,我将保留那个答案并将其标记为正确答案,谢谢你的回答! - cmaynard
2
如果您使用CORS,您可能需要在服务器端将所有标头(Cache-Control、Pragma等)添加到“Access-Control-Request-Headers”中。否则,您会遇到CORS问题。这意味着您的请求将失败。 - Arikael

7

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 只会影响通过 Angular(通常是您的 Angular 服务中的 HttpClient)进行的请求,这些请求99%都是 API 调用。因此,我认为这个额外的检查是不必要的,除非您由于某种原因在服务层内请求静态内容。 - LambdaCruiser
@LambdaCruiser,是的,你说得很对。这并不是真正必需的。我还是把它留在这里,以防有人掉进同样的陷阱。 - AndrWeisR

1
如上所述,您可以使用http请求拦截器来修改或设置请求的新标头。 以下是在后期angular版本(Angular 4+)上设置http请求拦截器标头的更简单方法。这种方法只会设置或更新某个请求标头。这是为了避免删除或覆盖一些重要的标头,例如授权标头。
// 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

使用meta 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的第二个链接(What is httpinterceptor equivalent in angular2?)。警告:不能保证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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接