2017-08-02 63 views
6

Używam Ionic 2, który znajduje się na górze Angular 2.W jaki sposób mogę zezwolić na kontynuowanie propagacji mojego żądania HTTP za pośrednictwem mojej aplikacji po przechwyceniu błędu?

Używam usługi w mojej aplikacji do obsługi wszystkich żądań API. Niektóre z tych żądań mają nagłówek Authorization. Mój interfejs API może zwrócić kod 401, jeśli autoryzacja nie powiedzie się, a ja aktualnie zajmuję się tym, pokazując błąd użytkownikowi i kierując go do ekranu logowania. To wszystko działa świetnie i publikuję wydarzenie dla tych błędów, które subskrybuję gdzie indziej, aby obsłużyć logikę (ponieważ nie mogę używać kontrolerów nawigacyjnych w usłudze).

Kod api.js usługi:

@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, 
    public events: Events) { 

    // 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) 
     .catch((error: any) => { 
      if (error.status === 500) { 
      this.events.publish('api:generalError', error); 
      return Observable.throw(new Error(error.status)); 
      } 
      else if (error.status === 401) { 
      this.events.publish('api:unauthorized', error); 
      return Observable.throw(new Error(error.status)); 
      } 
     }); 
    }).share(); 
    } 
} 

Problem jest w dół do „Ładowanie ...” dialogowe, które mogę pokazać użytkownikowi, gdy żądanie get ma miejsce. Zanim zostanie wywołana metoda, utworzę okno dialogowe ładowania i odrzucę je w przypadku sukcesu lub niepowodzenia. Problem polega na tym, że nie mam żadnego zakresu tego wewnątrz api.js, aby go odrzucić, gdy złapie 401 lub 500.

Oto próbka mojego logiki wokół to:

let loader = this.loadingCtrl.create({ 
    content: "Please wait..." 
}); 
loader.present(); 

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

    this.currentItems = res.training_programmes; 

}, (err) => { 
    // Error 
    console.log(err); 
},() => { 
    loader.dismiss(); 
}); 

Nie sądzę, że to ważne, ale mam również usługi dla każdego podmiotu, który z kolei wywołuje api.js usługi. W powyższym przykładzie jest to this.trainingProgramme, który wygląda tak:

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

    seq 
     .subscribe(); 

    return seq; 
    } 

myślałem tak, jak zbliżył się to wszystko było poprawne, jednak nie widzę sposób mogę poradzić z „załadunku” problem.

Czy istnieje sposób mogę mieć metodę get dalszym ciągu mojej aplikacji nawet po błąd został złapany, tak, że mój kod loader.dismiss() jest prowadzony w prawidłowym zakresie?

Naprawdę nie chcę używać ładowarki wewnątrz usługi (nie jestem pewien, czy jestem w stanie to zrobić?), Ponieważ wygląda na to, że jest to zły projekt, i nie chcę zawsze wyświetlać programu ładującego, więc należy do kontrolera.

Odpowiedz

1

Potrzebny jest operator .finally, który będzie wykonywany zawsze, nawet jeśli wystąpi błąd w Obserwowalnym.

Połączenie zwrotne (ostatnie z połączenia) nie uruchomi się, jeśli wystąpi error.

Pamiętaj, aby podać go:

import 'rxjs/add/operator/finally'; 

... 

this.trainingProgramme.get_programmes() 
.finally(() => loader.dismiss()) 
.map(res => res.json()) 
.subscribe((res) => { 
    ... 
} 
, (err) => { 
    // Error 
    console.log(err); 
}); 

Nawiasem mówiąc, należy rozważyć aktualizację kodu z @angular/http do nowego HttpClient.

+0

Hmm, to nie działa. Dodałem konsolę console.log() do 'finally (() => console.log ('testing')', ale nic się nie dzieje - brak danych wyjściowych, tak samo jak poprzednio z pojawieniem się 401 i ekranem logowania pojawiającym się przy ładowaniu ... okno dialogowe nałożone :( – Mike

+0

Powinna działać tutaj metoda finally() Subskrybujesz to, co można zaobserwować w funkcji get_programmes(), nie powodując żadnych błędów. Być może ten subskrybent uruchamia obsługę błędów i błędów, zanim zajmie się błędem Czy mógłbyś usunąć tę linię i zobaczyć, czy jest jakaś różnica? – hagner

+0

To jest posortowane, dodatkowe pośrednie 'subscribe()' musiało wywołać to wcześniej. – Mike