W moim projekcie Angular 2 wykonuję wywołania API z usług, które zwracają Observable. Następnie kod wywołujący subskrybuje ten obserwowalny. Na przykład:
getCampaigns(): Observable<Campaign[]> {
return this.http.get('/campaigns').map(res => res.json());
}
Powiedzmy, że serwer zwraca 401. Jak mogę wyłapać ten błąd globalnie i przekierować do strony logowania / komponentu?
Dzięki.
Oto, co mam do tej pory:
// boot.ts
import {Http, XHRBackend, RequestOptions} from 'angular2/http';
import {CustomHttp} from './customhttp';
bootstrap(AppComponent, [HTTP_PROVIDERS, ROUTER_PROVIDERS,
new Provider(Http, {
useFactory: (backend: XHRBackend, defaultOptions: RequestOptions) => new CustomHttp(backend, defaultOptions),
deps: [XHRBackend, RequestOptions]
})
]);
// customhttp.ts
import {Http, ConnectionBackend, Request, RequestOptions, RequestOptionsArgs, Response} from 'angular2/http';
import {Observable} from 'rxjs/Observable';
@Injectable()
export class CustomHttp extends Http {
constructor(backend: ConnectionBackend, defaultOptions: RequestOptions) {
super(backend, defaultOptions);
}
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
console.log('request...');
return super.request(url, options);
}
get(url: string, options?: RequestOptionsArgs): Observable<Response> {
console.log('get...');
return super.get(url, options);
}
}
Komunikat o błędzie, który otrzymuję, to „backend.createConnection nie jest funkcją”
Odpowiedzi:
Opis
Najlepszym rozwiązaniem, jakie znalazłem, jest zastąpienie
XHRBackend
takiego stanu odpowiedzi HTTP401
i403
prowadzi do określonej akcji.Jeśli obsługujesz uwierzytelnianie poza aplikacją Angular, możesz wymusić odświeżenie bieżącej strony, tak aby wyzwolony został mechanizm zewnętrzny. Wyszczególniam to rozwiązanie w realizacji poniżej.
Możesz również przekazać do składnika w aplikacji, tak aby aplikacja Angular nie została ponownie załadowana.
Realizacja
Kątowy> 2.3.0
Dzięki @mrgoos, tutaj jest uproszczone rozwiązanie dla angular 2.3.0+ z powodu poprawki błędu w angular 2.3.0 (patrz wydanie https://github.com/angular/angular/issues/11606 ) rozszerzające bezpośrednio
Http
moduł.Plik modułu zawiera teraz tylko następującego dostawcę.
Innym rozwiązaniem przy użyciu routera i zewnętrzną usługę uwierzytelniania jest szczegółowo w poniższej GIST przez @mrgoos.
Angular przed 2.3.0
Następująca implementacja działa dla
Angular 2.2.x FINAL
iRxJS 5.0.0-beta.12
.Przekierowuje do bieżącej strony (plus parametr umożliwiający uzyskanie unikalnego adresu URL i uniknięcie buforowania), jeśli zostanie zwrócony kod HTTP 401 lub 403.
z następującym plikiem modułu.
źródło
catch()
nie została znaleziona. (smh)import "rxjs/add/operator/catch";
http
? Jedyną korzyścią, jaką widzę, jest to, że możesz po prostu podać go jako dostawcę:{ provide: XHRBackend, useClass: AuthenticationConnectionBackend }
podczas gdy zastępując Http, musisz napisać bardziej niezręczny koduseFactory
i ograniczyć się, wywołując „nowy” i wysyłając określone argumenty. WDYT? Odniesienie do drugiej metody: adonespitogo.com/articles/angular-2-extending-http-providerAngular 4.3+
Wraz z wprowadzeniem HttpClient pojawiła się możliwość łatwego przechwytywania wszystkich żądań / odpowiedzi. Ogólne użycie HttpInterceptors jest dobrze udokumentowane , zobacz podstawowe użycie i sposób zapewnienia przechwytywacza. Poniżej znajduje się przykład HttpInterceptor, który może obsłużyć błędy 401.
Zaktualizowano dla RxJS 6+
RxJS <6
źródło
Router
tutaj wydaje się nie działać. Na przykład chcę skierować moich użytkowników do strony logowania, gdy otrzymają kod 401-403, alethis.router.navigate(['/login']
dla mnie nie działa. To nic nie robiimport 'rxjs/add/operator/do';
po zaimportowaniu rxjs.Ponieważ interfejsy API frontendu wygasają szybciej niż mleko, z Angular 6+ i RxJS 5.5+, musisz użyć
pipe
:import { HttpInterceptor, HttpEvent, HttpRequest, HttpHandler, HttpErrorResponse } from '@angular/common/http'; import { Observable, throwError } from 'rxjs'; import { Injectable } from '@angular/core'; import { catchError } from 'rxjs/operators'; import { Router } from '@angular/router'; @Injectable() export class AuthInterceptor implements HttpInterceptor { constructor(private router: Router) { } intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { return next.handle(req).pipe( catchError((err: HttpErrorResponse) => { if (err.status === 401) { this.router.navigate(['login'], { queryParams: { returnUrl: req.url } }); } return throwError(err); }) ); } }
Aktualizacja dla Angular 7+ i RXJS 6+
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse } from '@angular/common/http'; import { Observable, of } from 'rxjs'; import { Injectable } from '@angular/core'; import { catchError } from 'rxjs/internal/operators'; import { Router } from '@angular/router'; @Injectable() export class AuthInterceptor implements HttpInterceptor { constructor(private router: Router) { } intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { return next.handle(request) .pipe( catchError((err, caught: Observable<HttpEvent<any>>) => { if (err instanceof HttpErrorResponse && err.status == 401) { this.router.navigate(['login'], { queryParams: { returnUrl: request.url } }); return of(err as any); } throw err; }) ); } }
źródło
error TS2322: Type 'Observable<{}>' is not assignable to type 'Observable<HttpEvent<any>>'.
jak.pipe
tam jest brak błędów jak.pipe
req
jest tak naprawdęrequest
- edycja jestOtrzymane
Observable
z każdej metody żądania jest typuObservable<Response>
.Response
Obiekt, posiadastatus
właściwości, które odbędzie się401
, jeśli serwer wrócił ten kod. Więc możesz chcieć to odzyskać przed zmapowaniem lub przekonwertowaniem.Jeśli chcesz uniknąć wykonywania tej funkcji przy każdym wywołaniu, być może będziesz musiał rozszerzyć
Http
klasę Angular 2 i wstrzyknąć własną implementację, która wywołuje funkcję parent (super
) dla zwykłejHttp
funkcjonalności, a następnie obsłużyć401
błąd przed zwróceniem obiektu.Widzieć:
https://angular.io/docs/ts/latest/api/http/index/Response-class.html
źródło
Angular 4.3+
Aby ukończyć odpowiedź The Gilbert Arenas Dagger :
Jeśli potrzebujesz przechwycić jakikolwiek błąd, zastosuj do niego obróbkę i prześlij go dalej w dół łańcucha (a nie tylko dodaj efekt uboczny
.do
), możesz użyć HttpClient i jego przechwytywaczy, aby zrobić coś takiego:źródło
Aby uniknąć problemu z cyklicznymi odwołaniami, który jest spowodowany wstawieniem usług takich jak „Router” do klasy pochodnej HTTP, należy użyć metody Injector po konstruktorze. Poniższy kod jest działającą implementacją usługi HTTP, która przekierowuje do trasy logowania za każdym razem, gdy interfejs API REST zwraca „Token_Expired”. Zauważ, że może być używany jako substytut dla zwykłego HTTP i jako taki nie wymaga zmiany niczego w już istniejących składnikach lub usługach aplikacji.
app.module.ts
providers: [ {provide: Http, useClass: ExtendedHttpService }, AuthService, PartService, AuthGuard ],
Extended-http.service.ts
import { Injectable, Injector } from '@angular/core'; import { Request, XHRBackend, RequestOptions, Response, Http, RequestOptionsArgs, Headers } from '@angular/http'; import { Observable } from 'rxjs/Observable'; import { Router } from '@angular/router'; import { AuthService } from './auth.service'; import 'rxjs/add/operator/catch'; import 'rxjs/add/observable/throw'; @Injectable() export class ExtendedHttpService extends Http { private router; private authService; constructor( backend: XHRBackend, defaultOptions: RequestOptions, private injector: Injector) { super(backend, defaultOptions); } request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> { if (typeof url === 'string') { if (!options) { options = { headers: new Headers() }; } this.setHeaders(options); } else { this.setHeaders(url); } console.log("url: " + JSON.stringify(url) +", Options:" + options); return super.request(url, options).catch(this.catchErrors()); } private catchErrors() { return (res: Response) => { if (this.router == null) { this.router = this.injector.get(Router); } if (res.status === 401 || res.status === 403) { //handle authorization errors //in this example I am navigating to login. console.log("Error_Token_Expired: redirecting to login."); this.router.navigate(['signin']); } return Observable.throw(res); }; } private setHeaders(objectToSetHeadersTo: Request | RequestOptionsArgs) { if (this.authService == null) { this.authService = this.injector.get(AuthService); } //add whatever header that you need to every request //in this example I could set the header token by using authService that I've created //objectToSetHeadersTo.headers.set('token', this.authService.getToken()); } }
źródło
Od Angular> = 2.3.0 możesz nadpisać
HTTP
moduł i wstrzyknąć swoje usługi. Przed wersją 2.3.0 nie można było używać wstrzykniętych usług z powodu podstawowego błędu.Stworzyłem streszczenie, aby pokazać, jak to się robi.
źródło
app.module
kod? Dzięki.HTTP
gdzie indziej?Kątowy> 4,3: ErrorHandler dla usługi podstawowej
źródło