Danislav Kamenov
Danislav Kamenov

Reputation: 233

Implement an error interceptor based on what is shown

I'm building a small Angular app as part of my training. I'm trying to implement an error interceptor based on what is shown in the Angular tutorial. The idea is that I want to handle the error and then return an observable so that my components still receive data.

Interceptor:

    import { Injectable } from '@angular/core';
import {
    HttpRequest,
    HttpHandler,
    HttpEvent,
    HttpInterceptor
} from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { ToastrService } from 'ngx-toastr';

@Injectable({
    providedIn: 'root'
})
export class ErrorInterceptor implements HttpInterceptor {

    constructor(private toastr: ToastrService) { }

    private handleError<T> (result?: T) {
        return (error: any): Observable<T> => {      
          // Log the error on the console.
          console.log(error);    
          // Display user friendly message.
          this.toastr.error(`${error.error.message}`, `${error.statusText}:`);      
          // Let the app keep running by returning an empty result.
          console.log(result);
          return of(result as T);
        };
    }

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return next
            .handle(request)
            .pipe(
                catchError(this.handleError<any>({data: {token: 'asd'}}))
            );
    }
}

Service:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';
import { Subscription, Observable, of, throwError } from 'rxjs';
import { tap, map, catchError } from 'rxjs/operators';

import { LoginInput } from '../models/login.input-model';
import { RegisterInput } from '../models/register.input-model';
import { ServerResponse } from '../../../core/models/server-response.model';
import { ToastrService } from 'ngx-toastr';

const root = '/api/';

@Injectable({
providedIn: 'root'
})
export class AuthService {
    private loginUrl: string = root + 'auth/login';
    private registerUrl: string = root + 'auth/signup';
    private login$: Subscription;
    private register$: Subscription;

constructor(
    private http: HttpClient,
    private router: Router,
    private toastr: ToastrService) { }


private saveToken(data) {
    localStorage.setItem('token', data.token);
}

login(payload: LoginInput): void {
    this.login$ = this.http
        .post<ServerResponse>(this.loginUrl, payload)
        .subscribe((loginData) => {
            console.log('loginData: ' + loginData);
            // this.router.navigate(['/home']);
        });
}

clearLoginSubscription() {
    if (this.login$) this.login$.unsubscribe();
}    

}

Expected behaviour: The interceptor handles the error and returns the result as an observable. The data is then emitted on subscribe. Actual behaviour: The interceptor handles the error and nothing else happens. No data is emitted on login/register subscribe.

Note: I have discovered that if I migrate the handleError function to the service and pipe it in the same way that I have in the interceptor. I do get the expected behaviour. It just doesn't work from the interceptor.

Upvotes: 1

Views: 1476

Answers (2)

Marcelo
Marcelo

Reputation: 21

For those who want to handle the error and then progagate it, just implement the The Catch and Rethrow Strategy.

Your handleError function must throw the error:

return throwError(result);

This way, your component can receive data but in the error callback, not in the success callback of subscribe().

Reference: https://blog.angular-university.io/rxjs-error-handling/

Upvotes: 0

Akj
Akj

Reputation: 7231

You can capture data as :

this.http
        .post<ServerResponse>(this.loginUrl, payload)
        .subscribe((loginData) => {
            console.log('loginData: ' + loginData);
            // this.router.navigate(['/home']);
        }, (err: HttpErrorReponse) => {
          console.log(err);
        };

http-intercepter.ts

    /**
     * 
     * @param req - parameter to handle http request
     * @param next - parameter for http handler
     */
    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        const started = Date.now();
        /**
         * Handle newly created request with updated header (if given)
         */
        return next.handle(req).do((event: HttpEvent<any>) => {
            /**
             * Sucessfull Http Response Time.
             */
            if (event instanceof HttpResponse) {
                const elapsed = Date.now() - started;
            }

        }, (err: any) => {
            /**
             * redirect to the error_handler route according to error status or error_code
             * or show a modal
             */
            if (err instanceof HttpErrorResponse) {
                console.log(err)
            }
        });
    }

Upvotes: 1

Related Questions