// cache.ts
export abstract class Cache {
abstract get(req: HttpRequest<any>): HttpResponse<any> | null;
abstract put(req: HttpRequest<any>, res: HttpResponse<any>): void;
}
// cache-entry.ts
export interface CacheEntry {
url: string;
response: HttpResponse<any>;
entryTime: number;
}
export const MAX_CACHE_AGE = 20000; // in milliseconds
// cache-map.service.ts
@Injectable()
export class CacheMapService implements Cache {
cacheMap = new Map<string, CacheEntry>();
get(req: HttpRequest<any>): HttpResponse<any> | null {
const entry = this.cacheMap.get(req.urlWithParams);
if (!entry) {
return null;
}
const isExpired = Date.now() - entry.entryTime > MAX_CACHE_AGE;
return isExpired ? null : entry.response;
}
put(req: HttpRequest<any>, res: HttpResponse<any>): void {
const entry: CacheEntry = {
url: req.urlWithParams,
response: res,
entryTime: Date.now()
};
this.cacheMap.set(req.urlWithParams, entry);
this.deleteExpiredCache();
}
private deleteExpiredCache() {
this.cacheMap.forEach((entry) => {
if (Date.now() - entry.entryTime > MAX_CACHE_AGE) {
this.cacheMap.delete(entry.url);
}
});
}
}
// caching-interceptor.ts
const CACHABLE_URL = '/api/booksSearch';
@Injectable()
export class CachingInterceptor implements HttpInterceptor {
constructor(private cache: CacheMapService) {}
intercept(req: HttpRequest<any>, next: HttpHandler) {
if (!this.isRequestCachable(req)) {
return next.handle(req);
}
const cachedResponse = this.cache.get(req);
if (cachedResponse !== null) {
return of(cachedResponse);
}
return next.handle(req).pipe(
tap((event) => {
if (event instanceof HttpResponse) {
this.cache.put(req, event);
}
})
);
}
private isRequestCachable(req: HttpRequest<any>) {
return req.method === 'GET' && req.url.indexOf(CACHABLE_URL) > -1;
}
}
// logging-interceptor.ts
@Injectable()
export class LoggingInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler) {
const startTime = Date.now();
let status: string;
return next.handle(req).pipe(
tap(
(event) => {
status = '';
if (event instanceof HttpResponse) {
status = 'succeeded';
}
},
(error) => (status = 'failed')
),
finalize(() => {
const elapsedTime = Date.now() - startTime;
const message = `${req.method} ${req.urlWithParams} ${status} in ${elapsedTime}ms`;
this.logDetails(message);
})
);
}
private logDetails(msg: string) {
console.log(msg);
}
}
// index.ts
export const httpInterceptorProviders = [
{ provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: CachingInterceptor, multi: true }
];
// app.module.ts
@NgModule({
providers: [
httpInterceptorProviders,
CacheMapService,
{ provide: Cache, useClass: CacheMapService }
]
})
export class AppModule {}