Poiché ho iniziato con angular2 ho impostato i miei servizi per restituire Observable of T. Nel servizio avrei la chiamata map(), e i componenti che utilizzano questi servizi utilizzerebbero semplicemente subscribe() per attendere la risposta. Per questi semplici scenari non avevo davvero bisogno di scavare in rxjs quindi tutto andava bene.Gestione dei token di aggiornamento utilizzando rxjs
Ora desidero ottenere quanto segue: utilizzo l'autenticazione Oauth2 con token di aggiornamento. Voglio creare un servizio API che tutti gli altri servizi utilizzeranno e che gestirà in modo trasparente il token di aggiornamento quando viene restituito un errore 401. Quindi, nel caso di un 401, prima prelevo un nuovo token dall'endpoint OAuth2, quindi ritento la mia richiesta con il nuovo token. Di seguito è riportato il codice che funziona bene, con promesse:
request(url: string, request: RequestOptionsArgs): Promise<Response> {
var me = this;
request.headers = request.headers || new Headers();
var isSecureCall: boolean = true; //url.toLowerCase().startsWith('https://');
if (isSecureCall === true) {
me.authService.setAuthorizationHeader(request.headers);
}
request.headers.append('Content-Type', 'application/json');
request.headers.append('Accept', 'application/json');
return this.http.request(url, request).toPromise()
.catch(initialError => {
if (initialError && initialError.status === 401 && isSecureCall === true) {
// token might be expired, try to refresh token.
return me.authService.refreshAuthentication().then((authenticationResult:AuthenticationResult) => {
if (authenticationResult.IsAuthenticated == true) {
// retry with new token
me.authService.setAuthorizationHeader(request.headers);
return this.http.request(url, request).toPromise();
}
return <any>Promise.reject(initialError);
});
}
else {
return <any>Promise.reject(initialError);
}
});
}
Nel codice di cui sopra, authService.refreshAuthentication() si prendere il nuovo token e memorizzarlo in localStorage. authService.setAuthorizationHeader imposterà l'intestazione 'Autorizzazione' al token aggiornato in precedenza. Se osservi il metodo di cattura, vedrai che restituisce una promessa (per il token di aggiornamento) che alla fine restituirà un'altra promessa (per il 2 ° tentativo effettivo della richiesta).
Ho tentato di fare questo senza ricorrere a promesse:
request(url: string, request: RequestOptionsArgs): Observable<Response> {
var me = this;
request.headers = request.headers || new Headers();
var isSecureCall: boolean = true; //url.toLowerCase().startsWith('https://');
if (isSecureCall === true) {
me.authService.setAuthorizationHeader(request.headers);
}
request.headers.append('Content-Type', 'application/json');
request.headers.append('Accept', 'application/json');
return this.http.request(url, request)
.catch(initialError => {
if (initialError && initialError.status === 401 && isSecureCall === true) {
// token might be expired, try to refresh token
return me.authService.refreshAuthenticationObservable().map((authenticationResult:AuthenticationResult) => {
if (authenticationResult.IsAuthenticated == true) {
// retry with new token
me.authService.setAuthorizationHeader(request.headers);
return this.http.request(url, request);
}
return Observable.throw(initialError);
});
}
else {
return Observable.throw(initialError);
}
});
}
Il codice di cui sopra non fa quello che mi aspetto: nel caso di una risposta 200, restituisce correttamente la risposta. Tuttavia, se cattura il 401, recupererà con successo il nuovo token, ma il subscribe wil alla fine recupererà un osservabile invece della risposta. Immagino che questo sia l'Observable non eseguito che dovrebbe fare il tentativo.
Mi rendo conto che tradurre il modo promettente di lavorare sulla libreria rxjs non è probabilmente il modo migliore per andare, ma non sono stato in grado di cogliere la cosa "tutto è un flusso". Ho provato alcune altre soluzioni coinvolgendo flatmap, retryWhen etc ... ma non sono andato lontano, quindi un po 'di aiuto è apprezzato.
Ha, questo funziona :) Ho provato a utilizzare il metodo flatMap, ma apparentemente non nel modo corretto. Grazie Paul! Ps: quello che trovo abbastanza disordinato in rxjs è che tendono a nascondere questi metodi in file con nomi diversi. Perché il metodo flatMap funzioni, ho dovuto importare il file "mergeMap" ... – Davy
@Davy Posso chiedere quale versione stai usando? Se è il progetto di ReactiveX, è ancora in fase beta, quindi la documentazione è ancora carente mentre tutte le funzionalità sono state completate. – paulpdaniels
Questo: https://github.com/ReactiveX/RxJS (il pacchetto "rxjs" npm). – Davy