3

This is my error interceptor class. I need to throw error error to ccomponent class observable method: i haved checked throwError(error) is now deprecated, but there is no new Error(HttpErrorResponse)

@Injectable()
export class HttpErrorInterceptor implements HttpInterceptor {
    constructor(private toastr: ToastrService,private authService: AuthService,
        private router:Router) {
    }

    intercept( request: HttpRequest<any>, next: HttpHandler ): Observable<HttpEvent<any>> {
      return next.handle(request)
          .pipe(
              catchError((error: HttpErrorResponse) => {

                debugger
                  let message = '';
                  if (error.error instanceof ErrorEvent) {
                      // handle client-side error
                      message = `Error: ${error.error.message}`;
                      this.toastr.error(message);
                  } else {
                      // handle server-side error
                      debugger
                    
                      message = `Error: ${ error?.error?.Message || error?.statusText}`; 
                      if(!error.status)
                      {
                          this.toastr.error('Not able connect to server');                        
                      }

else if ([400].includes(error.status) && error.error?.Message === 'Session Expired') {

                     this.toastr.error("Session Expired");
                      this.authService.logout();
                     
                  }
                     .....

                    else if ([404].includes(error.status)) {
                      
                          this.router.navigate(['404']);  
                    }  
                   
                    else
                    {                  
                        this.toastr.error(message); 
                    } 
                  }
                 
                 return throwError(() => error) //If i throw errror like this it is coming error inteceptor agian
              })
          )
  }

}

component

getEditCollectionById(id:string)
  {
     debugger
     this.collectionService.getEditCollectionById(id).pipe(takeUntil(this.unsubscribe$)).subscribe({
       next: (result: any)  => {            
         if (result) {          
          
            this.collection=result;

         }
         else {
            this.close();
         }
       },
       error: (error:any) => {
           // If i throw error in interceptor it is not coming here
          this.goToDetail(this.collectionId);
       }
     });   

  }

service

getEditCollectionById(id: string): Observable<ICollection> {
          
      return  this.httpClient.get<Result<ICollection>>(baseUrl + '/Collection/GetEditCollectionById'+ `/${id}`) 
                  .pipe(map((res:any)=>{  res.data.valueDate = new Date(res.data.valueDate);       
                             return res.data;
                          })
                );
   }

i need to throw error in interceptor class. i am getting 400 error from server. i need show error message from interceptor class and i need to throw error to controller method.

EDIT:

Error Debug

enter image description here

EDIT: afer debugging infigured out its happening becuase of

 logout()  {
        
        debugger
        this.httpClient.post<any>(`${baseUrl}/Auth/revoke-token`, {}, { withCredentials: true })
        .subscribe(); 
             
        this.stopRefreshTokenTimer();
        this.setUserValue(null);
       this.router.navigate(['login']);
    }

Is there an update i need to do in this method?

Ajt
  • 1,719
  • 1
  • 20
  • 42

2 Answers2

6

If you want to pass an error to the component, and don't want to use state management to share an error response between classes, try tapping it in the interceptor like

import {
  HttpErrorResponse,
  HttpEvent,
  HttpHandler,
  HttpInterceptor,
  HttpRequest,
} from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';

@Injectable()
export class HttpErrorInterceptor implements HttpInterceptor {
  intercept(
    request: HttpRequest<unknown>,
    next: HttpHandler
  ): Observable<HttpEvent<unknown>> {
    return next.handle(request).pipe(
      tap({
        next: () => null,
        error: (error: HttpErrorResponse) => {
          console.log(
            'the interceptor has caught an error, process it here',
            error
          );
        },
      })
    );
  }
}

another option is to use throwError

import {
  HttpErrorResponse,
  HttpEvent,
  HttpHandler,
  HttpInterceptor,
  HttpRequest,
} from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, throwError } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';

@Injectable()
export class HttpErrorInterceptor implements HttpInterceptor {
  intercept(
    request: HttpRequest<unknown>,
    next: HttpHandler
  ): Observable<HttpEvent<unknown>> {
    return next.handle(request).pipe(
      catchError((error: HttpErrorResponse) => {
        console.warn(
          'the interceptor has caught an error, process it here',
          error
        );
        return throwError(() => error);
      })
    );
  }
}

import { HttpClient, HttpErrorResponse, HttpEvent } from '@angular/common/http';
import { Component, VERSION } from '@angular/core';
import { Observable, of } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  public name = 'Angular ' + VERSION.major;

  public error?: HttpErrorResponse;

  constructor(private readonly http: HttpClient) {
    this.getData().subscribe();
  }

  public getData() {
    return this.http.get('xxx').pipe(
      catchError(
        (error: HttpErrorResponse, caught: Observable<HttpEvent<unknown>>) => {
          console.error(
            'the component has caught an error, process it here',
            error
          );
          this.error = error;
          return of();
        }
      )
    );
  }
}

<!-- the component template -->

<hello name="{{ name }}"></hello>
<div>Caught error: {{ error?.message }}</div>

<p>Start editing to see some magic happen :)</p>

enter image description here

see live here:

PS: how to handle error in the logout method

logout()  {
        this.httpClient.post<any>(`${baseUrl}/Auth/revoke-token`, {}, { withCredentials: true })
        .pipe(catchError(error => {
          // do something with the error here
          return of();
        }),
        tap(() => {
          this.stopRefreshTokenTimer();
          this.setUserValue(null);
          this.router.navigate(['login']);
        }))
        .subscribe();
    }
rfprod
  • 231
  • 2
  • 8
  • Thanks .actullay i am using calling service methd from component..in my case what i have to change? – Ajt Jan 08 '22 at 16:30
  • @Ajt please check my answer again, I have updated it. See postscript in the end of my comment. – rfprod Jan 08 '22 at 16:35
  • Actually in previous version it was woring fine..is there any other option without tap method means using catchError – Ajt Jan 08 '22 at 16:38
  • @Ajt I don't think so. When you use catchError and return a new error within it, or return the caught observable that errored, it will go circles. When you use the catchError operator you tell your code to stop processing an errored observable. So, you should use catchError only when you want to stop processing the error. – rfprod Jan 08 '22 at 16:42
  • whether i can use this.router.navigate(['404']); within tap – Ajt Jan 08 '22 at 16:45
  • @Ajt yes, you can. When you use the tap operator, you tap into the stream and can perform any side effect. But beware, your code can get messy (as a result your application can behave unpredictably over time) is you misuse tapping into streams. – rfprod Jan 08 '22 at 16:49
  • Please check https://stackoverflow.com/questions/61156294/angular-interceptor-handle-http-error-and-retry all r used throwerror ..is i can use ? – Ajt Jan 08 '22 at 16:51
  • @Ajt hm, seems like you can. But it will be throwError(() => error) with rxjs 7. Otherwise I see a deprecation warning in the IDE. – rfprod Jan 08 '22 at 16:55
  • Yes ..after upgrading to rx js 7 if i throw error it looping...can u pls help – Ajt Jan 08 '22 at 16:57
  • @Ajt is it looping for you in this example https://stackblitz.com/edit/angular-ivy-kcqcjf?file=src%2Fapp%2Ferror.interceptor.ts? – rfprod Jan 08 '22 at 17:03
  • Yes..same issue..lopping – Ajt Jan 08 '22 at 17:10
  • the error throwing beacuse of logout methhod ..how to hanle error in logout mthod – Ajt Jan 09 '22 at 00:43
  • @Ajt please check the postscript in my answer, I have updated it – rfprod Jan 15 '22 at 15:47
1

Check on this sample code

import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { tap } from 'rxjs/operators';
import { AccountService } from '../services/account.service';
import { AlertService } from '../services/alert.service';

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
    constructor(private accountService: AccountService, private alertService: AlertService) {}

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return next.handle(request).pipe(
            tap({
                next: () => null,
                error: (err: HttpErrorResponse) => {
                    if ([401, 403].includes(err.status) && this.accountService.signInResponse)
                        this.accountService.signOut(); // auto logout if 401 or 403 response returned from api

                    const error = err.error?.message || err.status;
                    this.alertService.error(error);
                    return throwError(error);
                },
            })
        );
    }
}
Bercove
  • 987
  • 10
  • 18