angular - Hur kan jag fånga 401:or från en delad HTTP-förfrågan i min tjänsteleverantör?
Jag använder Ionic 2 för att skapa en mobilapp. Ionic 2 använder Angular 2.
Min logik är ganska enkel. Jag har en api-tjänst som har get() och post()-metoder för att skicka/hämta data med autentiseringshuvuden och get_no_auth() och post_no_auth() för förfrågningar utan autentiseringshuvuden.
Dessa metoder anropas från andra tjänster för specifika delar av appen (user, training_programme, etc).
Allt detta fungerar bra men jag behöver fånga 401-HTTP-statuskoder från API:et och vidta lämpliga åtgärder.
Min api.js tjänst:
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();
}
}
Jag kallar detta via en leverantör som är specifik för varje enhet. Så för träningsprogram:
get_programmes() {
let seq = this.api.get('training-programmes');
seq
.subscribe();
return seq;
}
Och jag kallar detta från min kontrollenhet:
this.trainingProgramme.get_programmes()
.map(res => res.json())
.subscribe((res) => {
loader.dismiss();
this.currentItems = res.training_programmes;
}, (err) => {
loader.dismiss();
});
Jag skulle helst inte hantera 401 i min controller, eftersom det skulle leda till duplicerad kod över hela min app. Istället skulle jag vilja fånga alla 401-statuskoder i api.js-tjänstefilen.
Hur kan jag göra det? Jag försökte lägga till ett catch-uttryck till http.get, men det leder till fel.
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<{}>'.
Svar #1
Det enklaste sättet att hantera felet är att använda ett catch-uttryck som du försökte. För att få det att fungera måste du returnera en observable från din felhanterare (felmeddelandet säger att du saknar en return-sats).
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);
}
Svar #2
Om det är ett löfte skulle det se ut ungefär så här:
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.');
}
});
