I'm trying to implement HttpRequest
caching using HttpInterceptor
as per the documentation by angular 4.3. But I'm getting an error. Here is my code:
caching.interceptor.ts
import { HttpRequest, HttpResponse, HttpInterceptor, HttpHandler, HttpEvent } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/do';
import 'rxjs/add/observable/of';
abstract class HttpCache {
abstract get(req: HttpRequest<any>): HttpResponse<any>|null;
abstract put(req: HttpRequest<any>, resp: HttpResponse<any>): void;
}
@Injectable()
export class CachingInterceptor implements HttpInterceptor {
constructor(private cache: HttpCache) {}
intercept(req: HttpRequest<any>, next: HttpHandler) : Observable<HttpEvent<any>> {
if(req.method !== 'GET'){
return next.handle(req);
}
const cachedResponse = this.cache.get(req);
if(cachedResponse){
return Observable.of(cachedResponse);
}
return next.handle(req).do(event => {
if(event instanceof HttpResponse){
this.cache.put(req, event);
}
})
}
}
Here CachingInterceptor
works as an interceptor for http request/response. And I've created module a which looks like:
app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component/app.component';
import { HomePage } from './pages/home.page/home.page';
import { ProductsPage } from './pages/products.page/products.page';
import { AboutUsPage } from './pages/about-us.page/about-us.page';
import { UsersPage } from './pages/users.page/users.page';
import { DemoPage } from './pages/demo.page/demo.page';
import { appRouter } from './app.router/app.router';
import { CachingInterceptor } from './caching.interceptor/caching.interceptor';
import { AppService } from './app.service/app.service';
@NgModule({
imports: [ BrowserModule, HttpClientModule, appRouter ],
declarations: [ AppComponent, HomePage, ProductsPage, DemoPage, AboutUsPage, UsersPage ],
providers: [ {
provide: HTTP_INTERCEPTORS,
useClass: CachingInterceptor,
multi: true
}, AppService ],
bootstrap: [ AppComponent ]
})
export class AppModule {}
Token is also provided in providers[] of module. This is as per the documentation by angular 4.3. But still i'm getting error like:
error
ERROR Error: Uncaught (in promise): Error: No provider for HttpCache!
Error: No provider for HttpCache!
at injectionError (reflective_errors.ts:71)
I have 2 questions:
HttpCache
is an abstract class, then why is it injected like a service?If you're looking for a super-simple cache-all-the-things implementation:
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { Observable } from "rxjs/Observable";
@Injectable()
export class CacheInterceptor implements HttpInterceptor {
private cache: { [name: string]: AsyncSubject<HttpEvent<any>> } = {};
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (request.method !== "GET") {
return next.handle(request);
}
const cachedResponse = this.cache[request.urlWithParams] || null;
if (cachedResponse) {
return cachedResponse.delay(0);
}
const subject = this.cache[request.urlWithParams] = new AsyncSubject<HttpEvent<any>>();
next.handle(request).do(event => {
if (event instanceof HttpResponse) {
subject.next(event);
subject.complete();
}
}).subscribe(); // must subscribe to actually kick off request!
return subject;
}
Note that this has been updated from the original method. The original intercept
method had a bug - if multiple requests of the identical url were attempted prior to the first returning, multiple requests would still hit the server.
This solution allows only one request to be passed through to the server.
The original solution is below for posterity. (Not recommended.)
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (request.method !== "GET") {
return next.handle(request);
}
const cachedResponse = this.cache[request.urlWithParams] || null;
if (cachedResponse) {
return Observable.of(cachedResponse);
}
return next.handle(request).do(event => {
if (event instanceof HttpResponse) {
this.cache[request.urlWithParams] = event;
}
});
}
From what I can tell your issue is that this is an abstract class
abstract class HttpCache {
abstract get(req: HttpRequest<any>): HttpResponse<any>|null;
abstract put(req: HttpRequest<any>, resp: HttpResponse<any>): void;
}
You would need to implement this class and it's methods in order to create an instance of it to use in your CachingInterceptor
class
export class HttpCacheService implements HttpCache {
get(req: HttpRequest<any>): HttpResponse<any>|null {
// Some logic
}
put(req: HttpRequest<any>, resp: HttpResponse<any>): void {
//Some logic
}
}
Then use HttpCacheService
in your CachingInterceptor
class.
But why not just store the requests in some sort of array if you are trying to cache them? This article may be a good starting point on how to accomplish what you are trying to do.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With