2016-11-05 8 views
7

Mam @ ngrx/store i efekty działają dobrze, jednak po prostu zdałem sobie sprawę, że będzie wiele wywołań API (w efektach), a jeśli którykolwiek z tych zwrotów błąd 401 powinienem przekierować użytkownika na stronę logowania. Mój problem polega na tym, że nie chcę tego sprawdzać w każdym pojedynczym efekcie, to byłby tonowy dodatkowy kod dla tego samego. Powiedzmy na przykład mam kod jak poniżej:Angular 2, przechwytywanie 401 w @ ngrx/effects

efektu Sample

@Effect() getMe$ = this.actions$ 
    .ofType(GET_ME) 
    .map(action => action.payload) 
    .switchMap(payload => this.userService.me() 
     .map(res => ({ type: GET_ME_SUCCESS, payload: res })) 
     .catch(() => Observable.of({ type: GET_ME_FAILURE })) 
    ); 

userService.me()

me(): Observable<User> { 
    return this.apiService.get(`/auth/me`); 
} 

apiService.get()

get(endpoint: string): Observable<any> { 
    return this.http.get(`${this.base}${endpoint}`, this.options()) 
    .map(res => res.json()); 
} 

Działa to doskonale, ale nie jestem pewien, jak sobie z tym poradzić, gdy API zwróci . Gdzie powinienem przekierować użytkownika globalnie w takim przypadku? Czy powinienem utworzyć akcję dla tej sprawy? Gdzie zatem powinienem wysłać tę akcję? Czy robię to zupełnie źle?

Każda pomoc we właściwym kierunku zostałaby doceniona!

Odpowiedz

12

Błędy, które są emitowane z Http będą zawierać właściwość status (ustawioną na kod statusu HTTP), jeśli są to błędy odebrane z serwera.

Jeśli to stan błędu w swoich działaniach awaryjnych usług opartych na protokole HTTP:

@Effect() getMe$ = this.actions$ 
    .ofType(GET_ME) 
    .map(action => action.payload) 
    .switchMap(payload => this.userService.me() 
     .map(res => ({ type: GET_ME_SUCCESS, payload: res })) 
     .catch(error => Observable.of({ 
      type: GET_ME_FAILURE, 
      payload: { errorStatus: error.status } 
     })) 
    ); 

Można następnie napisać efekt ogólnego przeznaczenia, który wygląda na wszystkich działań i przekierowuje jeśli zawierają błąd 401 :

@Effect() errorStatus401$ = this.actions$ 
    .map(action => action.payload) 
    .filter(payload => payload && payload.errorStatus === 401) 
    .switchMap(payload => { 
     this.router.navigate(['/login']); 
     return Observable.empty(); 
    }); 

Lub, jeśli użyć @ngrx/router-store:

import { go } from '@ngrx/router-store'; 
... 

@Effect() errorStatus401$ = this.actions$ 
    .map(action => action.payload) 
    .filter(payload => payload && payload.errorStatus === 401) 
    .map(payload => go(['/login'])); 

Jeśli są dodatkowe działania, które chcesz wykonać przed nawigowania można emitować wiele działań z użyciem concat:

@Effect() errorStatus401$ = this.actions$ 
    .map(action => action.payload) 
    .filter(payload => payload && payload.errorStatus === 401) 
    .switchMap(payload => Observable.concat({ type: 'CLEAR_TOKEN' }, go(['/login']))); 
+0

Dobrze, że wydaje się działać, dzięki! Jeszcze jedno pytanie, jeśli nie masz nic przeciwko: czy istnieje sposób na wywołanie innej akcji (CLEAR_TOKEN) przed nawigacją? Observables to dla mnie coś nowego. :) – Andrew

+1

Tak, to możliwe. Zaktualizowałem odpowiedź. – cartant