Кэширование Angular 6 с помощью Http Interceptor - оператор Map в коде каким-то образом мутирует кэшированный HttpResponse. - PullRequest
0 голосов
/ 24 сентября 2018

Недавно я получил кэш, работающий в приложении Angular 6 с использованием HttpInterceptor.Он записывает ответы в память и обслуживает их, если они существуют с TTL.Однако у меня есть проблема, когда программный код, который делает запросы Http, использует оператор карты для изменения полученных данных, это каким-то образом сохраняется в кэш.Как я могу сделать так, чтобы мои кэшированные объекты HttpResponse не были видоизменены каким-либо образом после извлечения их с сервера и сохранения их в кэше?

Я создал стек-блиц, который демонстрирует проблему:

https://stackblitz.com/edit/angular-caching-mutation

Когда ответ подается из кэша, он уже будет разбит на массив, как если бы операция отображения произошла до того, как ответ сервера был сохранен в кэше.

Служба получения, выполняющая HTTP-вызов и изменяющий ответ:

getData() {
return this.http.get('https://jsonplaceholder.typicode.com/todos/1')
  .pipe(map(next => {
    next['title'] = next['title'].split('u');
    return next;
  }));;
}

Перехватчик:

const TTL = 5;

@Injectable()
export class CacheInterceptor implements HttpInterceptor {
  constructor(private cache: GetCacheService) {
  }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (!(request.method === 'GET')) {
      return next.handle(request);
    } else {
      console.log('Checking cache for data.');
      const cachedResponse = this.cache.get(request.url);
      return cachedResponse ? of(cachedResponse) : this.handleRequest(request, next);
    }
  }

  private handleRequest(
    request: HttpRequest<any>,
    next: HttpHandler
  ) {
    return next.handle(request).pipe(tap(event => {

      if (event instanceof HttpResponse) {
        this.cache.set(request.url, event, TTL);
      }
    }));
  }
}

Служба кэширования:

@Injectable({
              providedIn: 'root'
            })
export class GetCacheService {
  private cache = new Map<string, [Date, HttpResponse<any>]>();

  constructor() {
  }

  get(key: string): HttpResponse<any> {
    const tuple = this.cache.get(key);
    if (!tuple) {
      return null;
    }

    const expires = tuple[0];
    const httpResponse = tuple[1];

    // check if cached HttpResponse is expired, if it is, delete
    // it and respond as if we had nothing cached
    const now = new Date();
    if (expires && expires.getTime() < now.getTime()) {
      this.cache.delete(key);
      return null;
    }
    console.log('Retrieved from cache!');
    console.log(httpResponse);
    console.log('The above cached HttpResponse object has been mutated by the mapping function in fetcher.service.ts');
    return httpResponse;
  }

  set(key: string, value: HttpResponse<any>, ttl = null) {
    if (ttl) {
      const expires = new Date();
      expires.setSeconds(expires.getSeconds() + ttl);
      this.cache.set(key, [expires, value]);
    } else {
      this.cache.set(key, [null, value]);
    }
    return true;
  }

}

1 Ответ

0 голосов
/ 25 сентября 2018

Внутри Cache Service, get ()

Вы возвращаете исходный объект кеша, который ссылается на тот же экземпляр объекта, и он будет видоизменяться, если его изменить.Вы можете использовать этот код

get(key: string): HttpResponse<any> {
   .....
   return Object.assign({}, httpResponse);
}
...