2016-04-15 13 views
7

Desidero effettuare una chiamata a un server che può restituire un'autorizzazione fallita (401) con la classe HTTP di Angular2.Come rilevare un errore su una richiesta, quindi aprire una modale, quindi riprovare quando la modale si chiude con RxJS

Il flusso della richiesta dovrebbe essere simile che:

  • L'utente effettua una richiesta al server con myService.getSomething() sottoscrivere()
  • Se il server restituisce un 401:. Aprire un finestra modale che chiede all'utente le sue credenziali.
  • L'utente accede con successo nuovamente dentro l'applicazione
  • si chiude modali ed esegue un callback
  • Il callback dovrebbe ritentare la richiesta iniziale (myService.getSomething(). Iscriviti())

Ecco quello che ho per il momento:

export class MyService { 
    // ... 
    public getSomething(): Observable<Response> { 
     return this.http.get(url, options).catch((res: any, ob: any) => this.errorHandler(res, ob)); 
    } 

    public errorHandler(res: Response, ob: Observable<any>): Observable<Response> { 
     if (res.status === 401) { 
      this.modalService.open(new ModalConfig({ 
       content: LoginModalComponent, 
       close:() => { ob.retry(1); console.log("weow") } // <=close is the callback that should initiate the retry action on the initial request. 
      })); 
     } 
     else { 
      return Observable.throw(res.json()); 
     } 
    } 
} 

doSomething() viene utilizzato come quella: doSomething().map((r) => r.json()).subscribe((r) => ....)

Aggiornamento 1

ho modificato il mio codice a guardare come la soluzione di @Thierry Templier.

private errorHandler(res: Response, ob: Observable<any>): Observable<Response> { 
    if (res.status === 401) { 
     let closedSubject = new Subject(); 
     this.modalService.open(new ModalConfig({ 
      content: LoginModalComponent, 
      close:() => { closedSubject.next(res);} // I also tried .complete(), .next(null), .next(true), .next(false) 
     })); 
     return ob.retryWhen(() => closedSubject); 
    } 
    else { 
     return Observable.throw(res.json()); 
    } 
} 

Purtroppo ancora non funziona. Il retryWhen viene eseguito immediatamente e non attende la chiamata di closedSubject.next(). Quindi avvia un ciclo infinito, inviando spam all'Osservable originale (la funzione getSomething()).

Aggiornamento 2

ho creato un plunker per dimostrare il ciclo infinito:

https://plnkr.co/edit/8SzmZlRHvi00OIdA7Bga

Attenzione: l'esecuzione del plunker sarà spammare la tua console con il 'test' stringa

Aggiornamento 3

In seguito alla risposta corretta di Thierry, ho cercato di trovare un modo per non utilizzare il campo sorgente poiché è protetto. Dopo aver chiesto sul tracker dei problemi di rxjs di rendere pubblico il campo, un collaboratore ha risposto con una soluzione migliore.

public get(url: string, options?: RequestOptionsArgs): Observable<Response> { 
    return super.get(url, options).retryWhen((errors: any) => this.errorHandler(errors)); 
} 
private errorHandler(errors): any { 
    return errors.switchMap((err) => { 
     if (err.status === 401) { 
      let closedSubject = new Subject(); 
      this.modalService.open(new ModalConfig({ 
       content: LoginModalComponent, 
       close:() => { closedSubject.next(err); } 
      })); 
      return <any>closedSubject; 
     } 
     else { 
      return Observable.throw(err.json()); 
     } 
    }); 
} 

Evito di usare .atch in modo da non dover utilizzare il campo sorgente.

+0

quindi tutto funziona adesso? – user3743222

+0

Sì, funziona. –

risposta

7

Penso che avete bisogno di restituire qualcosa di osservabile anche in caso di errore 401:

public errorHandler(res: Response, ob: Observable<any>): Observable<Response> { 
    if (res.status === 401) { 
     let closedSubject = new Subject(); 
     this.modalService.open(new ModalConfig({ 
      content: LoginModalComponent, 
      close:() => { 
       closedSubject.next(); 
     })); 
     return ob.retryWhen(() => closedSubject); 
    } 
    else { 
     return Observable.throw(res.json()); 
    } 
} 

consulta questo articolo per ulteriori dettagli: https://jaxenter.com/reactive-programming-http-and-angular-2-124560.html.

Modifica

Il problema è che il secondo parametro del catch richiamata non è la fonte osservabile. Questa fonte corrisponde osservabili al valore della sua proprietà source:

return ob.source.retryWhen((errors) => closedSubject); 

Vedere la plunkr di lavoro: https://plnkr.co/edit/eb2UdF9PSMhf4Dau2hqe?p=preview.

+0

Mi sembra che questo sia vicino alla risposta ma non funziona ancora. Quando il sever lancia un 401, errorHandler entra in un ciclo infinito. Riprova la richiesta senza attendere la chiusura del modal. –

+1

Non so cosa hai fatto nel tuo modal di accesso ma suppongo che tu debba impostare qualcosa nella tua richiesta prima di eseguirla di nuovo. Per questo motivo, penso che non dovresti usare il retry ma eseguire nuovamente la richiesta in base al suo url e all'opzione aggiornata (ad esempio, l'intestazione 'Authorization') ... –

+0

Il problema è che il retryWhen viene risolto subito . Non sta aspettando la chiusura di CloseSubject.next(). –

Problemi correlati