ionic 3 Define the header of the default Ionic storage queries

advertisements

This question is a bit similar Ionic 2 - Get token from Storage value and set Header before HTTP Request ( not a duplicate)

But the issue is connected with returning a value from local storage.

I need to set the default header(Authorization token) for all requests. Must be a typical problem, but can't find a solution. And most of the available info - about setting it for each request. Which is simple, but not very reasonable. - why set for each request if possible to set for all.

i tried:

case #1:

import {Injectable} from "@angular/core";
import {BaseRequestOptions, RequestOptions, RequestOptionsArgs} from "@angular/http";
import { Storage } from '@ionic/storage';

    @Injectable()
    export class MyRequestOptions extends BaseRequestOptions {
      constructor(private storage: Storage) {
        super();
        this.headers.set('Content-Type', 'application/json');
      }
      merge(options?: RequestOptionsArgs): RequestOptions {
        const newOptions = super.merge(options);

        console.log("setting auth header");

        function setAuthHeader(storage) {
          return storage.get('jwt_token').then(value => {
            newOptions.headers.set('Authorization',`Bearer ${value}`);
            return newOptions;
          });
        }

        return setAuthHeader(this.storage).then(()=>{
          return newOptions;
        })
      }
    }

in this case it's just not compiling, but it's to show what do i want to do.

case #2:

@Injectable()
export class MyRequestOptions extends BaseRequestOptions {
  constructor(private storage: Storage) {
    super();
    this.headers.set('Content-Type', 'application/json');
  }
  merge(options?: RequestOptionsArgs): RequestOptions {
    const newOptions = super.merge(options);

    console.log("setting auth header");
    return this.getApiToken().flatMap( data => {
      newOptions.headers.set('Authorization',`Bearer ${data}`);
      return newOptions;
    });
  }

  getApiToken(): Observable<RequestOptions> {
    return Observable.fromPromise(this.storage.get('jwt_token'));
  }
}

(it is similar to mentioned SO topic), but .flatMap() throwing a error:

argument of type '(data: Headers) => RequestOptions' is not assignable to parameter of type '(value: Headers, index: number) => ObservableInput<{}>'. Type 'RequestOptions' is not assignable to type 'ObservableInput<{}>'. Type 'RequestOptions' is not assignable to type 'ArrayLike<{}>'. Property 'length' is missing in type 'RequestOptions'.

case #3:

let api_token = await this.storage.get('jwt_token'); -> await simply isn't work here.

Any idea?


I've actually implemented this inside my Ionic Application, please see below:

I created a custom Http Interceptor,

http.interceptor.ts

import { Events } from 'ionic-angular';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import 'rxjs';
import { Storage } from '@ionic/storage';
import {Http, RequestOptionsArgs, Response, RequestOptions, ConnectionBackend, Headers} from '@angular/http';
import {Observable} from 'rxjs/Observable';
import {Storage} from '@ionic/storage';
import 'rxjs/add/operator/toPromise';
import 'rxjs/add/operator/map';

export class HttpInterceptor extends Http {
  constructor(connectionBackend: ConnectionBackend, requestOptions: RequestOptions, public storage: Storage) {
    super(connectionBackend, requestOptions);
  }

  public get(url: string, options?: RequestOptionsArgs): Observable<Response> {
    return Observable.fromPromise(
      this.getRequestOptionArgs(options)
    ).mergeMap((options) => {
      return super.get(url, options)
    });
  }

  public post(url: string, body: string, options?: RequestOptionsArgs): Observable<Response> {
    return Observable.fromPromise(
      this.getRequestOptionArgs(options)
    ).mergeMap((options) => {
      return super.post(url, body, options)
    })
  }

  public put(url: string, body: string, options?: RequestOptionsArgs): Observable<Response> {
    return Observable.fromPromise(
      this.getRequestOptionArgs(options)
    ).mergeMap((options) => {
      return super.put(url, body, options)
    })
  }

  public delete(url: string, options?: RequestOptionsArgs): Observable<Response> {
    return Observable.fromPromise(
      this.getRequestOptionArgs(options)
    ).mergeMap((options) => {
      return super.delete(url, options)
    });
  }

  private getRequestOptionArgs(options?: RequestOptionsArgs) {
    return this.storage.get('token').then((token) => {
      if (options == null) {
        options = new RequestOptions();
      }

      if (options.headers == null) {
        options.headers = new Headers();
      }

      if (token !== null) {
        options.headers.append('Authorization', 'Bearer ' + token);
      }
      options.headers.append('Content-Type', 'application/json');

      return options;
    });
  }
}

Inside my app.module.ts

app.module.ts

import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {IonicApp, IonicModule} from 'ionic-angular';
import {Storage, IonicStorageModule} from '@ionic/storage';
import {HttpModule, XHRBackend, RequestOptions, Http} from '@angular/http';
import {HttpInterceptor} from '../auth/http.interceptor';
import {SplashScreen} from "@ionic-native/splash-screen";
import {StatusBar} from '@ionic-native/status-bar';
import {Keyboard} from '@ionic-native/keyboard';
import {InAppBrowser} from '@ionic-native/in-app-browser';

export function httpInterceptorFactory(xhrBackend: XHRBackend, requestOptions: RequestOptions, storage: Storage) {
  return new HttpInterceptor(xhrBackend, requestOptions, storage);
}

@NgModule({
  declarations: [
    ...
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp, {mode: 'md'}),
    IonicStorageModule.forRoot(),
    HttpModule
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    ...
  ],
  providers: [StatusBar, SplashScreen, Keyboard, InAppBrowser, //these are just things I needed in my app
    {
      provide: Http,
      useFactory: httpInterceptorFactory,
      deps: [XHRBackend, RequestOptions, Storage]
    }

  ]
})

export class AppModule {
}

Then inside my provider I used it like a regular http request

app.provider.ts

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/Rx';
import { CONFIG } from '../config/app.config';

@Injectable()
export class AppProvider {
  private baseUrl:string = CONFIG.apiEndpoint;

  constructor(private http: Http) {

  }

  public getSomething():Observable<any> {
    let url:string = this.baseUrl + 'some endpoint';

    return this.http.get(url).map((res:Response) => res.json());
  }
}

Hopefully this helps