Lalit Kushwah
Lalit Kushwah

Reputation: 4049

Load component from interceptor

I am planning to handle network error at central point. So I am using interceptors for the same. Now I have a challenge, suppose I have got session time out in response (of any request) and I want to redirect user to the login page. In Ionic NavController is the solution to load any component (page). How can I use this NavController to load page/component in Interceptor.

Here is my interceptor:

export class PostInterceptor implements HttpInterceptor {    
 intercept(req: HttpRequest<any>, next:HttpHandler):Observable<HttpEvent<any>> 
{
    let postReq = req.clone();
    let authenticationToken = localStorage.getItem('token');
    postReq.headers.set('Content-Type','application/json');

    if(authenticationToken != null) {
       postReq.headers.append('Bearer',authenticationToken);
    }
    return next.handle(postReq).do(event => {
      if (event instanceof HttpResponse) {
        /*const time = new Date().toLocaleString();
        console.log(`Request happened at ${time}.`);*/
        if(event.body.loggedIn != null) {
          //loggedIn = false
          if(!event.body.loggedIn) {

          }
          else {
            //Do Nothing
            console.log('LoggedIn True')
          }
        }
        else {
          console.log(event.body);
          AppModule.map.set('prevRequest',postReq);
        }
      }
    })
  }
}

Upvotes: 1

Views: 2218

Answers (2)

DigitalMystery
DigitalMystery

Reputation: 575

I ended up using the built in Events to handle my redirects and timeouts. I have an HTTP interceptor for outgoing calls and a response interceptor shown in the second snippet below. Only showing the response interceptor for this post. Here is the shameless blog post I created:

Ionic: Handle 401 & 408 HTTP Responses in Interceptor

First in the app.component you will need to subscribe to the events you want to create. As you can tell below, on an unauthorized I remove authentication and then set the root to my login page. I also show a popup. On a timeout I just display a popup.

this.events.subscribe("unauthorized:requestError",
  () => {
    this.authenticationService.removeAuthentication()
      .then(() => {
        this.nav.setRoot(LoginPage);

        const alert = this.alertCtrl.create({
          title: "Unauthorized",
          subTitle: "Your credentials have expired.  Please log back in.",
          buttons: ["OK"]
        });
        alert.present();
      });
  });

this.events.subscribe("timeout:requestError",
  () => {
    const alert = this.alertCtrl.create({
      title: "Request Timeout",
      subTitle: "Please refresh or try your request again.",
      buttons: ["OK"]
    });
    alert.present();
  });

Next in the interceptor I use the Events in Ionic Angular to publish them when needed. You will need to import them and then inject them into the constructor.

import { Injectable } from "@angular/core";
import { Events } from "ionic-angular";
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpResponse, HttpErrorResponse } from "@angular/common/http";
import { Observable } from "rxjs/Observable";

@Injectable()
export class ResponseInterceptor implements HttpInterceptor {
  constructor(
    public events: Events
  ){}

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

    return next.handle(request).do((event: HttpEvent<any>) => {
      if (event instanceof HttpResponse) {
        // do stuff with response if you want
      }
    }, (err: any) => {
      if (err instanceof HttpErrorResponse) {
        if (err.status === 401) {
          this.events.publish("unauthorized:requestError");
        } else if (err.status === 408) {
          this.events.publish("timeout:requestError");
        }
      } else if (err.name === "TimeoutError") {
        this.events.publish("timeout:requestError");
      }
    });

  }
}

I will say from above I have an extra timeout in mine which is when I reject calls after 15 seconds in my outgoing interceptor. That is err.name === "TimeoutError". They still call the same Event though.

Upvotes: 1

David Dal Busco
David Dal Busco

Reputation: 8692

I would use an Observable and Subject to do so.

For example http://jasonwatmore.com/post/2016/12/01/angular-2-communicating-between-components-with-observable-subject

Applied to your case:

First you create a service:

import {Injectable} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import {Subject} from 'rxjs/Subject';

@Injectable()
export class MyService {
    private requestInterceptedSource: Subject<number> = new Subject<number>();
    requestIntercepted: Observable<number> = this.requestInterceptedSource.asObservable();

    constructor() {
    }

    getInterceptedSource(): Subject<number> {
        return this.requestInterceptedSource;
    }

}

Then you use this service in your interceptor in order to propagate the error:

 export class PostInterceptor implements HttpInterceptor { 

       constructor(private myService: MyService) {}

       intercept(req: HttpRequest<any>, next:HttpHandler):Observable<HttpEvent<any>> 
       {
         // I didn't copy/pasted your code...of course you should still use it ;)

         // Here we start to propagate the error
    this.myService.getInterceptedSource().next(err.status);
       }
 }

Finally in your app.component.ts you watch (subscribe) to the subject in order to redirect (I would say setRoot since there was an error) to the page you want in case of error:

 private subscription: Subscription;

constructor(private app: App, private myService: MyService) {

    this.subscription = this.myService.requestIntercepted.subscribe((errorCode: number) => {this.app.getRootNav().setRoot('YourPage'});

}

ngOnDestroy() {
    // unsubscribe to ensure no memory leaks
    this.subscription.unsubscribe();
}

Upvotes: 1

Related Questions