在Angular HttpClient中捕获错误
我有一个如下所示的数据服务:
@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 Error: Uncaught (in promise): [object Object] 来自 core.es5.js 如何在我的情况下处理它?
回答(9)
随着 HTTPClient
API的到来,不仅更换了 Http
API,而且添加了一个新的 HttpInterceptor
API .
AFAIK的目标之一是为所有HTTP传出请求和传入响应添加默认行为 .
因此,假设您要添加 default error handling behavior ,将 .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);
}
}
从您的服务方法而不是 Observables
返回 Promises
是另一个错误的决定 .
还有一条建议:如果您使用的是 TYPE 脚本,那么请开始使用它的类型部分 . 您将失去该语言的最大优势之一:了解您正在处理的值的类型 .
如果您想要一个角度服务的好例子,请看一下at the following gist .
让我更新acdcjunior关于使用HttpInterceptor和最新RxJs功能(v.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;
})
);
}
}
在@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
}
});
相当简单(与之前的API相比) .
来自(复制并粘贴)Angular official guide的来源
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!');
}
);
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)));
}
通过使用Interceptor,您可以捕获错误 . 以下是代码:
@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");
}
}
});
}
}
你可以更喜欢this blog ..给它一个简单的例子 .
2 years ago
根据您的需要,您有一些选择 . 如果要基于每个请求处理错误,请在您的请求中添加
catch
. 如果要添加全局解决方案,请使用HttpInterceptor
.打开here the working demo plunker以获取以下解决方案 .
tl;博士
在最简单的情况下,您只需添加
.catch()
或.subscribe()
,如:但是有更多细节,见下文 .
方法(本地)解决方案:记录错误并返回回退响应
如果只需要在一个地方处理错误,可以使用
catch
并返回默认值(或空响应),而不是完全失败 . 你也不需要.map
只是为了施放,你可以使用通用函数 . 资料来源:Angular.io - Getting Error Details .所以,一个通用的
.get()
方法,就像:即使URL上的服务状况不佳,处理错误也会使应用程序继续运行 .
当您想要为每个方法返回特定的默认响应时,此每请求解决方案很有用 . 但是,如果您只关心错误显示(或具有全局默认响应),则更好的解决方案是使用拦截器,如下所述 .
运行working demo plunker here .
高级用法:拦截所有请求或响应
再次,Angular.io guide显示:
当然,这可以用来以非常简单的方式处理错误(demo plunker here):
Providing your interceptor: 简单地声明上面的
HttpErrorInterceptor
并不会导致您的应用使用它 . 您需要将它作为拦截器提供wire it up in your app module,如下所示:Note: 如果同时存在错误拦截器和一些本地错误处理,则很可能不会触发本地错误处理,因为拦截器在到达本地错误处理之前始终会处理错误 .
运行working demo plunker here .