dev_054
dev_054

Reputation: 3718

Interceptor making two calls in API

I'm working on a project and everything is working well... and when I request something to the API (like the example below) it only sends, obviously, a single request:

this.httpClient.get<MyInterface>('my_api').subscribe(data => this.items = data);

But I wanted to take advantage of Interceptors in the new HttpClient, so I started to search examples and I found this.

I've defined everything as in the post:

progress-interceptor.ts:

@Injectable()
export class ProgressInterceptor implements HttpInterceptor {

  progress$: Observable<number | null>;
  private progressSubject: Subject<number | null>;

  constructor() {
    this.progressSubject = new ReplaySubject<number | null>(1);
    this.progress$ = this.progressSubject.asObservable();
  }

  intercept<T>(req: HttpRequest<T>, next: HttpHandler): Observable<HttpEvent<T>> {
    const reportingRequest = req.clone({ reportProgress: true });
    const handle = next.handle(reportingRequest);

    handle.subscribe((event: HttpEvent<T>) => {
      switch (event.type) {
        case HttpEventType.Sent:
          this.progressSubject.next(undefined);
          break;

        case HttpEventType.DownloadProgress:
        case HttpEventType.UploadProgress:
          if (event.total) {
            this.progressSubject.next(Math.round((event.loaded / event.total) * 100));
          }
          break;

        case HttpEventType.Response:
          this.progressSubject.next(100);
          break;

        default:
        break;
      }
    });

    return handle;
  }
}

progress-component.ts:

export class ProgressComponent implements OnInit {

  progressPercentage$: Observable<number>;

  @Input() color: ProgressBarColor = 'primary';

  @ViewChild(MdProgressBar) private progressBar: MdProgressBar;

  constructor(private interceptor: ProgressInterceptor) { }

  ngOnInit(): void {
    this.progressPercentage$ = this.interceptor.progress$
        .map(progress => {
          if (isNil(progress)) {
            this.setMode('indeterminate');

            return 0;
          } else {
            this.setMode('determinate');

            return progress;
          }
        });
  }

  private setMode(mode: ProgressBarMode) {
    this.progressBar.mode = mode;
  }
}

app.module.ts:

const interceptor = new ProgressInterceptor();

@NgModule({
  // ...
  providers: [
    { provide: ProgressInterceptor, useValue: interceptor },
    { provide: HTTP_INTERCEPTORS, useValue: interceptor, multi: true }
  ]
})

The problem is: now, with this interceptor, when I do any request, it hits the API twice. Why? I suspect that's because the subscribe inside the intercept() of ProgressInterceptor.

How to solve this problem?

Upvotes: 0

Views: 2668

Answers (1)

PerfectPixel
PerfectPixel

Reputation: 1968

Your guess is correct, by subscribing to the handle within the interceptor, you are triggering the observable chain again and therefore trigger the API call again.

In your case the appropriate operator is .do(...) (see the docs) as you only want to observe what is happening inside of the chain but have no intention to manipulate anything within it.

It should look something like this:

intercept<T>(req: HttpRequest<T>, next: HttpHandler): Observable<HttpEvent<T>> {
  const reportingRequest = req.clone({ reportProgress: true });
  return next.handle(reportingRequest)
             .do((event: HttpEvent<T>) => {
                switch (event.type) {
                  case HttpEventType.Sent:
                    this.progressSubject.next(undefined);
                    break;

                  case HttpEventType.DownloadProgress:
                  case HttpEventType.UploadProgress:
                    if (event.total) {
                      this.progressSubject.next(Math.round((event.loaded / event.total) * 100));
                    }
                    break;

                  case HttpEventType.Response:
                    this.progressSubject.next(100);
                    break;

                  default:
                  break;
                }
            });
}

Upvotes: 3

Related Questions