2

I am using Ionic 2 to create a mobile app. Ionic 2 uses Angular 2.

My logic is quite straightforward. I have an api service which has get() and post() methods for sending/retrieving data with auth headers and get_no_auth() and post_no_auth() for requests without auth headers.

These methods are called from other services for specific parts of the app (user, training_programme, etc).

This all works fine but I need to catch 401 HTTP status codes from the API and take appropriate action.

My api.js service:

import { Injectable } from '@angular/core';
import { Http, Headers, RequestOptions, URLSearchParams } from '@angular/http';
import { Storage } from '@ionic/storage';
import 'rxjs/add/operator/map';
import 'rxjs/Rx';
import { Observable } from 'rxjs';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/catch';

@Injectable()
export class Api {
  base_url: string = 'https://*****.com';
  url: string = this.base_url + '/api/v1';
  authurl: string = this.base_url + '/oauth/token';
  grant_type: string = 'password';
  client_id: string = '1';
  client_secret: string = '*****';
  access_token: string;

  constructor(
    public http: Http, 
    private storage: Storage) {

    // Grab access token and store it
    storage.get('access_token').then((val) => {
      this.access_token = val;
    });
  }

  // Performs a GET request with auth headers
  get(endpoint: string, params?: any) {
    if(!params) {
      params = [];
    }
    params['grant_type'] = this.grant_type;
    params['client_id'] = this.client_id;
    params['client_secret'] = this.client_secret;

    let headers: Headers = this.getHeaders();

    return this.getApiToken().flatMap(data => {

      headers.append('Authorization', 'Bearer ' + data);

      let options = new RequestOptions({ headers: headers });

      // Support easy query params for GET requests
      if (params) {
        let p = new URLSearchParams();
        for (let k in params) {
          p.set(k, params[k]);
        }
        // Set the search field if we have params and don't already have
        // a search field set in options.
        options.search = !options.search && p || options.search;
      } 

      return this.http.get(this.url + '/' + endpoint, options);
    }).share();
  }
}

I call this via a provider specific for each entity. So for training programmes:

  get_programmes() {
    let seq = this.api.get('training-programmes');

    seq
      .subscribe();

    return seq;
  }

And I call this from my controller:

this.trainingProgramme.get_programmes()
    .map(res => res.json())
    .subscribe((res) => {

    loader.dismiss();
    this.currentItems = res.training_programmes;

}, (err) => {
    loader.dismiss();
});

I would rather not handle the 401 inside my controller, as it would lead to duplicate code all over my app. Instead, I would like to catch any 401 status codes in the api.js service file.

How can I do that? I tried adding a catch statement to the http.get, but that leads to errors.

Argument of type '(e: any) => void' is not assignable to parameter of type '(err: any, caught: Observable<Response>) => ObservableInput<{}>'. Type 'void' is not assignable to type 'ObservableInput<{}>'.
Mike
  • 8,767
  • 8
  • 49
  • 103
  • This may help: [handle 401 error globally](https://stackoverflow.com/questions/34934009/handling-401s-globally-with-angular) – hiper2d Aug 01 '17 at 15:02

2 Answers2

1

The easiest way to handle the error is to use a catch statement like you tried. To make it work you will have to return an observable from your error handler (the error message is telling you that you are lacking a return statement).

private handleError(error: any) {
    let errMsg: string;
    if (error.status === 401) {
        errMsg = 'Got error 401';
    }
    else {
        errMsg = 'Got some other error';
    }
    console.error(errMsg);
    return Observable.throw(errMsg);
} 
hagner
  • 1,050
  • 9
  • 12
0

If it's a promise it would look something like this:

this.UserService.insert(user).then(
            (response) => {
                this.nav.pop();
            },
            (err) => {
                if (err.status == 400) {
                    let body = JSON.parse(err._body);
                    if (body.message) {
                        this.presentAlert(err.statusText, body.message);
                    }
                    else {
                        this.presentAlert(err.statusText, 'Something went wrong: Failed to save User to database.');
                    }
                }
                else {
                    this.presentAlert(err.statusText, 'Something went wrong: Failed to save User to database.');
                }
            });
Alex Pappas
  • 2,377
  • 3
  • 24
  • 48