How to perform caching http get request in angular 5?

百般思念 提交于 2019-12-10 23:25:56

问题


    import { Injectable } from '@angular/core';
    import { HttpClient, HttpErrorResponse } from '@angular/common/http';
    import { Observable } from 'rxjs';
    import { catchError } from 'rxjs/operators';

    @Injectable()
    export class ApiService {

      constructor(private http: HttpClient) { }

      // get API request
      public apiGetRequest(url: any): Observable<any> {
        return this.http.get(url)
          .pipe(
            catchError(this.handleError('apiGetRequest'))
          );
      }
    }

I am using angular 5 with rxjs version 5.5.6 I am trying to cache multiple http get request.


回答1:


Try something like this:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';

@Injectable()
export class ApiService {

  private _someData: any;

  constructor(private http: HttpClient) { }

  public apiGetRequest(url: any): Observable<any> {

    /**
     * Returns cached data if exists.
     */
    if (this._someData) {
      return of(this._someData);
    }

    return this.http.get(url)
      .pipe(
        tap((someData: any) => {
          if (someData) {
            this._someData = someData;
          }
        }),
        catchError(this.handleError('apiGetRequest'))
      );
  }
}

In first time we fetch data from backend api, next times apiGetRequest() will be returns cached data.




回答2:


this way without rxjs 🧙‍♂️ but you can check this answer

@Injectable()
export class CashingService {

  private __cach = {}
  isCashed(url: string) {
    return this.__cach[url];
  }

  getData(url: string) {
    return this.__cach[url]
  }

  setData(url) {
    return (data) => {
      if (data && (data instanceof Error) === false)
        this.__cach[url] = data
    };

  }

  reset() {
    this.__cach = {};
  }

}

inject chaching service

@Injectable()
export class ApiService {

  constructor(private http: HttpClient , private _c:CachingService ) { }

  // get API request
  public apiGetRequest(url: any): Observable<any> {

    if (this._c.isCashed(url)){
     return of(this._c.getData(url));
    } else {
    return this.http.get(url)
      .pipe(
       tab(this._c.setData(url)),
       catchError(this.handleError('apiGetRequest'))
      );
    }

  }
}



回答3:


The right way to do it would be like this:

private varName$: Observable<T>;

private _apiGetRequest():Observable<T>{
 return this.http.get('endpoint');
}
public apiGetRequest():Observable<T>{
 if(!this.varName$){
  this.varName$ = this._apiGetRequest().pipe(shareReplay(1));
 }
 return this.varName$
}

This way, the private observable keeps the last emitted value




回答4:


This is cache service I'm using, it stores data in Redux store, but it's ease to change to any data holder you want.

@Injectable()
export class CacheManager {

  constructor(
    private store: NgRedux<IAppState>,
    private actions: GlobalActions) {
  }

  public get<T>(key: CacheKey, thisArg: any, dataRetriver: (...args: any[]) => T | Observable<T>, ...args: any[]): Observable<T> {
    if (this.isCached(key)) {
      return of(this.getFromCache<T>(key));
    }

    const data$: T | Observable<T> = dataRetriver.apply(thisArg, args);

    if (data$ instanceof Observable) {
      return data$.pipe(
        tap(result => this.addToCache(key, result))
      );
    }
    else {
      this.addToCache(key, data$);
      return of(data$);
    }
  }

  public clear(): void {
    this.store.dispatch(this.actions.clearCache());
  }

  private isCached(key: string): boolean {
    return this.cacheStorage[key] !== undefined;
  }

  private addToCache<T>(key: string, value: T): void {
    this.store.dispatch(this.actions.cache(key, value));
  }

  private getFromCache<T>(key: string): T {
    return <T>this.cacheStorage[key];
  }

  private get cacheStorage(): { [key: string]: any } {
    return this.store.getState().global.cache;
  }
}

and example to use:

// call without cache:
this.service.loadPeriods(param1, param2)
  .pipe(...)
  .subscribe(...)

// call with cache ("periods" is a cache key):
this.cache.get("periods", this.service, this.service.loadPeriods, param1, param2)
  .pipe(...)
  .subscribe(...);



回答5:


You can create internal key/value pair storage solution to cache the request. Here I am using Map() for this.

import { Injectable } from '@angular/core';
import { HttpEvent, HttpRequest, HttpHandler, HttpInterceptor, HttpResponse } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { tap, shareReplay } from 'rxjs/operators';

@Injectable()
export class CacheInterceptor implements HttpInterceptor {
  private cache = new Map<string, any>();

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (request.method !== 'GET') {
      return next.handle(request);
    }

    const cachedResponse = this.cache.get(request.url);
    if (cachedResponse) {
      return of(cachedResponse);
    }

    return next.handle(request).pipe(
      tap(event => {
        if (event instanceof HttpResponse) {
          this.cache.set(request.url, event);
        }
      })
    );
  }
}

Now You need to take care of cache invalidation. whenever data changes you need to invalidate.



来源:https://stackoverflow.com/questions/55136593/how-to-perform-caching-http-get-request-in-angular-5

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!