У меня есть служба данных, которая выглядит так:
& Лт;!- язык: машинопись - >
@Injectable()
export class DataService {
baseUrl = 'http://localhost'
constructor(
private httpClient: HttpClient) {
}
get(url, params): Promise<Object> {
return this.sendRequest(this.baseUrl + url, 'get', null, params)
.map((res) => {
return res as Object
})
.toPromise();
}
post(url, body): Promise<Object> {
return this.sendRequest(this.baseUrl + url, 'post', body)
.map((res) => {
return res as Object
})
.toPromise();
}
patch(url, body): Promise<Object> {
return this.sendRequest(this.baseUrl + url, 'patch', body)
.map((res) => {
return res as Object
})
.toPromise();
}
sendRequest(url, type, body, params = null): Observable<any> {
return this.httpClient[type](url, { params: params }, body)
}
}
Если я получаю ошибку HTTP (т.е. 404), я получаю неприятное консольное сообщение: Ошибка ERROR: Uncaught (in обещание): [объект Object] из core.es5.js Как мне справиться с этим в моем случае?
У вас есть несколько вариантов, в зависимости от ваших потребностей. Если вы хотите обрабатывать ошибки для каждого запроса, добавьте catch
к вашему запросу. Если вы хотите добавить глобальное решение, используйте HttpInterceptor
.
Откройте [ здесь рабочий демонстрационный плюнкер ][4] для решений ниже.
В простейшем случае вам просто нужно добавить .catch ()
или .subscribe ()
, например:
& Лт;!- язык: машинопись - >
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)
);
Но есть и другие детали, см. Ниже.
& Лт; br >
Если вам нужно обрабатывать ошибки только в одном месте, вы можете использовать catch
и вернуть значение по умолчанию (или пустой ответ) вместо полного отказа. Вам также не нужен .map
, просто чтобы разыграть, вы можете использовать общую функцию. Источник: Angular.io - Получение сведений об ошибке.
Таким образом, общий метод .get ()
будет похож на:
& Лт;!- язык: машинопись - >
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>();
});
}
}
Обработка ошибки позволит вашему приложению продолжить работу, даже если служба по URL находится в плохом состоянии.
Это решение для запроса полезно в основном, когда вы хотите вернуть определенный ответ по умолчанию для каждого метода. Но если вы заботитесь только о отображении ошибок (или имеете глобальный ответ по умолчанию), лучшим решением будет использование перехватчика, как описано ниже.
Запустите [ рабочий демонстрационный плюнкер здесь ][4].
& Лт; br >
Еще раз, Руководство Angular.io показывает:
Главной особенностью
@ angular / common / http
является перехватчик, возможность объявлять перехватчики, которые находятся между вашим приложением и бэкэндом. Когда ваше приложение делает запрос, перехватчики преобразуют его перед отправкой на сервер, и перехватчики могут преобразовать ответ на обратном пути до того, как ваше приложение увидит его. Это полезно для всего, от аутентификации до регистрации.
Который, конечно, можно использовать для обработки ошибок очень простым способом ([ demo plunker здесь ][4]):
& Лт;!- язык: машинопись - >
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>>();
});
}
}
Предоставление вашего перехватчика: Простое объявление HttpErrorInterceptor
выше не заставляет ваше приложение использовать его. Вам необходимо переключить его в модуль приложения, предоставив его в качестве перехватчика, следующим образом:
& Лт;!- язык: машинопись - >
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 {}
Примечание: Если у вас есть и перехватчик ошибок, и некоторая локальная обработка ошибок, естественно, что локальная обработка ошибок никогда не будет запущена, поскольку перехватчик всегда будет обрабатывать ошибку * до того, как она достигнет локальная обработка ошибок.
Запустите [ рабочий демонстрационный плюнкер здесь ][4].
[4]: https://plnkr.co/edit/ulFGp4VMzrbaDJeGqc6q?р = предварительный просмотр
С появлением API HTTPClient
был заменен не только API Http
, но и добавлен новый API HttpInterceptor
AFAIK одной из его целей является добавление поведения по умолчанию ко всем исходящим HTTP-запросам и входящим ответам.
Поэтому предположить, что вы хотите добавить поведение при обработке ошибок по умолчанию , добавив .catch ()
ко всем вашим возможным методам http.get/post/etc, смехотворно сложно поддерживать.
Это можно сделать следующим образом, например, с помощью HttpInterceptor
:
& Лт;!- язык: машинопись - >
import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse, HTTP_INTERCEPTORS } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { _throw } from 'rxjs/observable/throw';
import 'rxjs/add/operator/catch';
/**
* Intercepts the HTTP responses, and in case that an error/exception is thrown, handles it
* and extract the relevant information of it.
*/
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
/**
* Intercepts an outgoing HTTP request, executes it and handles any error that could be triggered in execution.
* @see HttpInterceptor
* @param req the outgoing HTTP request
* @param next a HTTP request handler
*/
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req)
.catch(errorResponse => {
let errMsg: string;
if (errorResponse instanceof HttpErrorResponse) {
const err = errorResponse.message || JSON.stringify(errorResponse.error);
errMsg = `${errorResponse.status} - ${errorResponse.statusText || ''} Details: ${err}`;
} else {
errMsg = errorResponse.message ? errorResponse.message : errorResponse.toString();
}
return _throw(errMsg);
});
}
}
/**
* Provider POJO for the interceptor
*/
export const ErrorInterceptorProvider = {
provide: HTTP_INTERCEPTORS,
useClass: ErrorInterceptor,
multi: true,
};
// app.module.ts
& Лт;!- язык: машинопись - >
import { ErrorInterceptorProvider } from 'somewhere/in/your/src/folder';
@NgModule({
...
providers: [
...
ErrorInterceptorProvider,
....
],
...
})
export class AppModule {}
Дополнительная информация для OP: Вызов http.get/post/etc без сильного типа не является оптимальным использованием API. Ваш сервис должен выглядеть так:
& Лт;!- язык: машинопись - >
// These interfaces could be somewhere else in your src folder, not necessarily in your service file
export interface FooPost {
// Define the form of the object in JSON format that your
// expect from the backend on post
}
export interface FooPatch {
// Define the form of the object in JSON format that your
// expect from the backend on patch
}
export interface FooGet {
// Define the form of the object in JSON format that your
// expect from the backend on get
}
@Injectable()
export class DataService {
baseUrl = 'http://localhost'
constructor(
private http: HttpClient) {
}
get(url, params): Observable<FooGet> {
return this.http.get<FooGet>(this.baseUrl + url, params);
}
post(url, body): Observable<FooPost> {
return this.http.post<FooPost>(this.baseUrl + url, body);
}
patch(url, body): Observable<FooPatch> {
return this.http.patch<FooPatch>(this.baseUrl + url, body);
}
}
Возврат «Обещаний» из ваших методов обслуживания вместо «Обозреваемых» - еще одно плохое решение.
И дополнительный совет: если вы используете сценарий TYPE , начните использовать его тип. Вы теряете одно из самых больших преимуществ языка: знать тип ценности, с которой вы имеете дело.
Если вы хотите, на мой взгляд, хороший пример углового обслуживания, взгляните на следующую суть.
Позвольте мне обновить ответ acdcjunior об использовании HttpInterceptor с последними RxJs функции (ст.6).
import { Injectable } from '@angular/core';
import {
HttpInterceptor,
HttpRequest,
HttpErrorResponse,
HttpHandler,
HttpEvent,
HttpResponse
} from '@angular/common/http';
import { Observable, EMPTY, throwError, of } from 'rxjs';
import { catchError } from 'rxjs/operators';
@Injectable()
export class HttpErrorInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
catchError((error: HttpErrorResponse) => {
if (error.error instanceof Error) {
// A client-side or network error occurred. Handle it accordingly.
console.error('An error occurred:', error.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 ${error.status}, body was: ${error.error}`);
}
// If you want to return a new response:
//return of(new HttpResponse({body: [{name: "Default value..."}]}));
// If you want to return the error on the upper level:
//return throwError(error);
// or just return nothing:
return EMPTY;
})
);
}
}
Довольно просто (по сравнению с тем, как это было сделано с предыдущим API).
Источник из (копировать и вставлять) Англовое официальное руководство
http
.get<ItemsResponse>('/api/items')
.subscribe(
// Successful responses call the first callback.
data => {...},
// Errors will call this callback instead:
err => {
console.log('Something went wrong!');
}
);
Для Angular 6+ .catch не работает напрямую с Observable. Вы должны использовать
.pipe(catchError(this.errorHandler))
Ниже код:
import { IEmployee } from './interfaces/employee';
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class EmployeeService {
private url = '/assets/data/employee.json';
constructor(private http: HttpClient) { }
getEmployees(): Observable<IEmployee[]> {
return this.http.get<IEmployee[]>(this.url)
.pipe(catchError(this.errorHandler)); // catch error
}
/** Error Handling method */
errorHandler(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
// A client-side or network error occurred. Handle it accordingly.
console.error('An error occurred:', error.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 ${error.status}, ` +
`body was: ${error.error}`);
}
// return an observable with a user-facing error message
return throwError(
'Something bad happened; please try again later.');
}
}
Для получения более подробной информации обратитесь к Угловое руководство для Http
Угловая служба обработки ошибок 8 HttpClient Пример
api.service.ts
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
import { Student } from '../model/student';
import { Observable, throwError } from 'rxjs';
import { retry, catchError } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class ApiService {
// API path
base_path = 'http://localhost:3000/students';
constructor(private http: HttpClient) { }
// Http Options
httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
}
// Handle API errors
handleError(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
// A client-side or network error occurred. Handle it accordingly.
console.error('An error occurred:', error.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 ${error.status}, ` +
`body was: ${error.error}`);
}
// return an observable with a user-facing error message
return throwError(
'Something bad happened; please try again later.');
};
// Create a new item
createItem(item): Observable<Student> {
return this.http
.post<Student>(this.base_path, JSON.stringify(item), this.httpOptions)
.pipe(
retry(2),
catchError(this.handleError)
)
}
........
........
}
Вы, вероятно, хотите иметь что-то вроде этого:
this.sendRequest(...)
.map(...)
.catch((err) => {
//handle your error here
})
Это также сильно зависит от того, как вы пользуетесь своим сервисом, но это основной случай.
Следуя ответу @acdcjunior, я так и реализовал его
служба:
get(url, params): Promise<Object> {
return this.sendRequest(this.baseUrl + url, 'get', null, params)
.map((res) => {
return res as Object
}).catch((e) => {
return Observable.of(e);
})
.toPromise();
}
звонящий:
this.dataService.get(baseUrl, params)
.then((object) => {
if(object['name'] === 'HttpErrorResponse') {
this.error = true;
//or any handle
} else {
this.myObj = object as MyClass
}
});
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
const PASSENGER_API = 'api/passengers';
getPassengers(): Observable<Passenger[]> {
return this.http
.get<Passenger[]>(PASSENGER_API)
.pipe(catchError((error: HttpErrorResponse) => throwError(error)));
}
Используя перехватчик, вы можете поймать ошибку. Ниже код:
@Injectable()
export class ResponseInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
//Get Auth Token from Service which we want to pass thr service call
const authToken: any = `Bearer ${sessionStorage.getItem('jwtToken')}`
// Clone the service request and alter original headers with auth token.
const authReq = req.clone({
headers: req.headers.set('Content-Type', 'application/json').set('Authorization', authToken)
});
const authReq = req.clone({ setHeaders: { 'Authorization': authToken, 'Content-Type': 'application/json'} });
// Send cloned request with header to the next handler.
return next.handle(authReq).do((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
console.log("Service Response thr Interceptor");
}
}, (err: any) => {
if (err instanceof HttpErrorResponse) {
console.log("err.status", err);
if (err.status === 401 || err.status === 403) {
location.href = '/login';
console.log("Unauthorized Request - In case of Auth Token Expired");
}
}
});
}
}
Вы можете предпочесть этот блог..приведенный простой пример для этого.
Если вы обнаружите, что не можете уловить ошибки с помощью какого-либо из решений, представленных здесь, возможно, сервер не обрабатывает запросы CORS.
В этом случае Javascript, гораздо менее угловой, может получить доступ к информации об ошибке.
Ищите предупреждения в вашей консоли, которые включают CORB
или Cross-Origin Read Blocking
.
Кроме того, синтаксис изменился для ошибок обработки (как описано в любом другом ответе). Теперь вы используете операторов, работающих на трубах, например так:
this.service.requestsMyInfo(payload).pipe(
catcheError(err => {
// handle the error here.
})
);