web-dev-qa-db-fra.com

Angular 5 HttpInterceptor Error Handling en appelant d'abord la gestion des erreurs de l'appelant

J'ai un HttpInterceptor global avec un bloc catch qui gère un HttpErrorResponse. Mais mon exigence est que lorsqu'un service effectue l'appel http et qu'il dispose également d'un gestionnaire d'erreurs, je veux que le gestionnaire d'erreurs du service s'éteigne en premier. S'il n'y a pas de gestionnaire d'erreur sur ce service, je veux que le gestionnaire d'erreur global HttpInterceptor le gère.

Exemple de code:

Intercepteur HTTP:

@Injectable()
export class ErrorHttpInterceptor implements HttpInterceptor {

constructor(private notificationService: NotificationService) {}

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req)
  .catch(
    error => {
      if (error instanceof HttpErrorResponse) {
        this.notificationService.error('Error', 'Error in handling Http request');
      }

      return Observable.empty<HttpEvent<any>>();
    }
  );
 }
}

Et l'appel de service:

updateUser(id, data) {
  return this.http
    .patch(`${API.userUrl}/${id}`,  {data: data})
    .subscribe(
      () => console.log('success'),
      (err) => this.notificationService.error('custom code to handle this')
     );
}

Dans ce cas, ErrorHttpInterceptor donne la notification, puis la gestion des erreurs userService donne également la notification d'erreur.

Mais dans mon cas d'utilisation, je veux que ErrorHttpIntercetor gère l'erreur uniquement si les abonnements sous-jacents ne gèrent pas l'erreur. Y a-t-il un moyen de faire cela?

8
Nani

Une façon de résoudre est de passer httpHeaders avec la requête:

 request() {  
    const url = 'GoalTree/GetById/'; 
    let headers = new HttpHeaders();
    headers = headers.append('handleError', 'onService');
    this.http.get(url, {headers: headers})
      .pipe(
      map((data: any) => {
        this.showError = false; 
      }),
      catchError(this.handleError)
      )
      .subscribe(data => {
        console.log('data', data);
      })
  }

Interceptor.ts:

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    this.spinnerService.show();

    return next.handle(req).do(
      (event: HttpEvent<any>) => {
        if (event instanceof HttpResponse) {
          this.spinnerService.hide();
        }
      },
      (err: any) => {
        if (req.headers.get('handleError') === 'onService') {
          console.log('Interceptor does nothing...');
        } else {
          console.log('onInterceptor handle ', err);
        }

      }
    );
  }

et vérifiez les en-têtes de demande dans le rappel d'erreur de interceptor. Mais dans cette solution, n'importe où Interceptors traite la demande avant les appels de service.

Exemple StackBlitz

2
Yerkon

C'est la meilleure solution que j'ai trouvée jusqu'à présent.

Dans votre intercepteur, créez une nouvelle propriété d'abonnement sur l'objet d'erreur, puis renvoyez votre erreur:

return next.handle(authReq).pipe(
    catchError((err: any, caught: Observable<HttpEvent<any>>) => {
        err.defaultHandler = timer(0).subscribe(() => {
            this.notify.error('Server Error', 'Server Error Message');
        });
        return throwError(err);
    })
);

Lorsque vous souhaitez réellement gérer l'erreur, désabonnez-vous de l'abonnement du minuteur créé ci-dessus:

this.authService.login(authRequest).subscribe((x) => {
  this.navigateDefaultRoute();
}, (err) => {
  // Prevent default error from being handled
  err.defaultHandler.unsubscribe();

  this.notify.error('Invalid Credentials', 'Email and password did not match');
});
0
Chris Fremgen