Masz kilka opcji, w zależności od potrzeb. Jeśli chcesz obsługiwać błędy na podstawie poszczególnych żądań, dodaj catch
do żądania. Jeśli chcesz dodać globalne rozwiązanie, użyj HttpInterceptor
.
Otwórz tutaj działający plunker demonstracyjny dla poniższych rozwiązań.
tl; dr
W najprostszym przypadku wystarczy dodać a .catch()
lub a .subscribe()
, na przykład:
import 'rxjs/add/operator/catch'; // don't forget this, or you'll get a runtime error
this.httpClient
.get("data-url")
.catch((err: HttpErrorResponse) => {
// simple logging, but you can do a lot more, see below
console.error('An error occurred:', err.error);
});
// or
this.httpClient
.get("data-url")
.subscribe(
data => console.log('success', data),
error => console.log('oops', error)
);
Ale jest więcej szczegółów na ten temat, patrz poniżej.
Rozwiązanie metody (lokalne): błąd dziennika i zwrot odpowiedzi rezerwowej
Jeśli potrzebujesz obsługiwać błędy tylko w jednym miejscu, możesz użyć catch
i zwrócić wartość domyślną (lub pustą odpowiedź) zamiast całkowicie zawieść. Nie potrzebujesz też .map
tylko do rzutowania, możesz użyć funkcji ogólnej. Źródło: Angular.io - Pobieranie szczegółów błędu .
Tak więc ogólna .get()
metoda wyglądałaby tak:
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from "@angular/common/http";
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/of';
import 'rxjs/add/observable/empty';
import 'rxjs/add/operator/retry'; // don't forget the imports
@Injectable()
export class DataService {
baseUrl = 'http://localhost';
constructor(private httpClient: HttpClient) { }
// notice the <T>, making the method generic
get<T>(url, params): Observable<T> {
return this.httpClient
.get<T>(this.baseUrl + url, {params})
.retry(3) // optionally add the retry
.catch((err: HttpErrorResponse) => {
if (err.error instanceof Error) {
// A client-side or network error occurred. Handle it accordingly.
console.error('An error occurred:', err.error.message);
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong,
console.error(`Backend returned code ${err.status}, body was: ${err.error}`);
}
// ...optionally return a default fallback value so app can continue (pick one)
// which could be a default value
// return Observable.of<any>({my: "default value..."});
// or simply an empty observable
return Observable.empty<T>();
});
}
}
Obsługa błędu umożliwi kontynuowanie działania aplikacji nawet wtedy, gdy usługa pod adresem URL jest w złym stanie.
To rozwiązanie na żądanie jest dobre głównie wtedy, gdy chcesz zwrócić określoną domyślną odpowiedź dla każdej metody. Ale jeśli zależy Ci tylko na wyświetlaniu błędów (lub masz globalną odpowiedź domyślną), lepszym rozwiązaniem jest użycie przechwytywacza, jak opisano poniżej.
Uruchom tutaj działającego demo plunkera .
Użycie zaawansowane: przechwytywanie wszystkich żądań lub odpowiedzi
Po raz kolejny przewodnik Angular.io pokazuje:
Główną cechą @angular/common/http
jest przechwytywanie, czyli możliwość deklarowania przechwytywaczy, które znajdują się pomiędzy aplikacją a zapleczem. Kiedy aplikacja wysyła żądanie, elementy przechwytujące przekształcają je przed wysłaniem do serwera, a przechwytywacze mogą przekształcić odpowiedź w drodze powrotnej, zanim aplikacja ją zobaczy. Jest to przydatne do wszystkiego, od uwierzytelniania po rejestrowanie.
Które oczywiście można wykorzystać do obsługi błędów w bardzo prosty sposób ( tutaj demo plunker ):
import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse,
HttpErrorResponse } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/of';
import 'rxjs/add/observable/empty';
import 'rxjs/add/operator/retry'; // don't forget the imports
@Injectable()
export class HttpErrorInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request)
.catch((err: HttpErrorResponse) => {
if (err.error instanceof Error) {
// A client-side or network error occurred. Handle it accordingly.
console.error('An error occurred:', err.error.message);
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong,
console.error(`Backend returned code ${err.status}, body was: ${err.error}`);
}
// ...optionally return a default fallback value so app can continue (pick one)
// which could be a default value (which has to be a HttpResponse here)
// return Observable.of(new HttpResponse({body: [{name: "Default value..."}]}));
// or simply an empty observable
return Observable.empty<HttpEvent<any>>();
});
}
}
Zapewnienie przechwytywacza: samo zadeklarowanie HttpErrorInterceptor
powyższego nie spowoduje, że aplikacja będzie go używać. Musisz połączyć go w module aplikacji, dostarczając go jako przechwytywacz w następujący sposób:
import { NgModule } from '@angular/core';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { HttpErrorInterceptor } from './path/http-error.interceptor';
@NgModule({
...
providers: [{
provide: HTTP_INTERCEPTORS,
useClass: HttpErrorInterceptor,
multi: true,
}],
...
})
export class AppModule {}
Uwaga: Jeśli masz zarówno przechwytywacz błędów, jak i pewną lokalną obsługę błędów, jest oczywiście prawdopodobne, że żadna lokalna obsługa błędów nigdy nie zostanie wyzwolona, ponieważ błąd będzie zawsze obsługiwany przez przechwytywacz, zanim dotrze do lokalnej obsługi błędów.
Uruchom tutaj działającego demo plunkera .
return this.httpClient.get<type>(...)
. a potem miećcatch...
gdzieś poza usługą, gdzie faktycznie to konsumuje, ponieważ tam będzie budował przepływ obserwowalny i może sobie z tym najlepiej poradzić.