我有一个如下所示的数据服务:
@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),我会得到一个讨厌的控制台消息: 错误:未捕获(承诺):来自 core.es5.js的 在我的情况下如何处理?
答案 0 :(得分:140)
根据您的需要,您有一些选择。如果您希望按请求处理错误,请在您的请求中添加catch
。如果要添加全局解决方案,请使用HttpInterceptor
。
打开here the working demo plunker以获取以下解决方案。
在最简单的情况下,您只需要添加.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)
);
但是有更多细节,见下文。
如果只需要在一个地方处理错误,可以使用catch
并返回默认值(或空响应),而不是完全失败。您也不需要.map
只是为了施放,您可以使用通用功能。来源:Angular.io - Getting Error Details。
因此,通用的.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上的服务状况不佳,处理错误也会让您的应用继续运行。
这种每请求解决方案很好,主要是当您想要为每个方法返回特定的默认响应时。但是,如果您只关心错误显示(或具有全局默认响应),则更好的解决方案是使用拦截器,如下所述。
再次,Angular.io guide显示:
@angular/common/http
的一个主要特性是拦截,即声明位于应用程序和后端之间的拦截器的能力。当您的应用程序发出请求时,拦截器会在将其发送到服务器之前对其进行转换,拦截器可以在应用程序看到之前将响应转换回来。这对于从身份验证到日志记录的所有内容都很有用。
当然,这可用于以非常简单的方式处理错误(demo plunker here):
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
并不会导致您的应用使用它。您需要wire it up in your app module将其作为拦截器提供,如下所示:
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 {}
注意:如果你有两个错误拦截器和一些本地错误处理,很自然地,可能不会触发任何本地错误处理,因为错误在到达本地错误处理之前,拦截器将始终处理它。
答案 1 :(得分:41)
随着HTTPClient
API的推出,不仅替换了Http
API,而且添加了新的HttpInterceptor
API。
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);
}
}
从您的服务方法而不是Promises
返回Observables
是另一个糟糕的决定。
还有一条建议:如果你使用 TYPE 脚本,那么就开始使用它的类型部分。您失去了该语言的最大优势之一:了解您正在处理的值的类型。
如果您想要一个角度服务的好例子,请查看at the following gist。
答案 2 :(得分:22)
请让我更新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;
})
);
}
}
答案 3 :(得分:3)
Angular 8 HttpClient错误处理服务Example
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)
)
}
........
........
}
答案 4 :(得分:2)
你可能想要这样的东西:
this.sendRequest(...)
.map(...)
.catch((err) => {
//handle your error here
})
这在很大程度上取决于您如何使用您的服务,但这是基本情况。
答案 5 :(得分:2)
相当简单(与之前的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!');
}
);
答案 6 :(得分:1)
关注@acdcjunior回答,这是我实现它的方式
服务:
suppressPackageStartupMessages(library(dplyr))
df <- data.frame(dest=c("IAH","IAH","MIA","BQN","ATL","ATL"),
avg_delay=c(13,24,35,-19,-31,8))
# average delay by destination
df %>%
group_by(dest) %>%
summarise(avg_delay = mean(avg_delay))
#> # A tibble: 4 x 2
#> dest avg_delay
#> <fctr> <dbl>
#> 1 ATL -11.5
#> 2 BQN -19.0
#> 3 IAH 18.5
#> 4 MIA 35.0
# sum of average delay by destination
df %>%
group_by(dest) %>%
summarise(avg_delay = sum(avg_delay))
#> # A tibble: 4 x 2
#> dest avg_delay
#> <fctr> <dbl>
#> 1 ATL -23
#> 2 BQN -19
#> 3 IAH 37
#> 4 MIA 35
呼叫者:
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();
}
答案 7 :(得分:1)
对于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.');
}
}
有关更多详细信息,请参阅Angular Guide for Http
答案 8 :(得分:0)
如果您发现自己无法使用此处提供的任何解决方案捕获错误,则可能是服务器未处理CORS请求。
在这种情况下,Java语言(比Angular少得多)可以访问错误信息。
在控制台中查找包含CORB
或Cross-Origin Read Blocking
的警告。
此外,处理错误的语法也已更改(如每个其他答案所述)。现在,您可以使用管道运算符,如下所示:
this.service.requestsMyInfo(payload).pipe(
catcheError(err => {
// handle the error here.
})
);
答案 9 :(得分:0)
通过使用拦截器,您可以捕获错误。下面是代码:
@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 ..给出一个简单的示例。
答案 10 :(得分:0)
更糟糕的是没有像样的堆栈跟踪,而您不能使用HttpInterceptor
来生成堆栈跟踪(希望经过纠正)。您得到的只是区域和rxjs的无用膨胀,而不是产生错误的行或类。
要执行此操作,您将需要在扩展的HttpClient
中生成堆栈,因此不建议在生产环境中执行此操作。
/**
* Extended HttpClient that generates a stack trace on error when not in a production build.
*/
@Injectable()
export class TraceHttpClient extends HttpClient {
constructor(handler: HttpHandler) {
super(handler);
}
request(...args: [any]): Observable<any> {
const stack = environment.production ? null : Error().stack;
return super.request(...args).pipe(
catchError((err) => {
// tslint:disable-next-line:no-console
if (stack) console.error('HTTP Client error stack\n', stack);
return throwError(err);
})
);
}
}