[angular] 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 오류 : Uncaught (in promise) : [object Object] from core.es5.js
내 경우 어떻게 처리합니까?
답변
필요에 따라 몇 가지 옵션이 있습니다. 요청별로 오류를 처리하려면 요청에를 추가 catch
하십시오. 글로벌 솔루션을 추가하려면 HttpInterceptor
.
열고 여기에 작업 데모 plunker 아래의 솔루션을.
tl; dr
가장 간단한 경우, 당신은 단지 추가해야합니다 .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 가이드 는 다음을 보여줍니다.
의 주요 기능
@angular/common/http
은 차단, 즉 애플리케이션과 백엔드 사이에있는 인터셉터를 선언하는 기능입니다. 애플리케이션이 요청을 할 때 인터셉터는이를 서버로 보내기 전에 변환하고 인터셉터는 애플리케이션이 응답을보기 전에 응답을 변환 할 수 있습니다. 이것은 인증에서 로깅까지 모든 것에 유용합니다.
물론 매우 간단한 방법으로 오류를 처리하는 데 사용할 수 있습니다 ( 여기에서 데모 플 런커 ).
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 {}
참고 : 오류 인터셉터와 일부 로컬 오류 처리 가 모두 있는 경우 , 로컬 오류 처리에 도달 하기 전에 오류가 항상 인터셉터에 의해 처리 되기 때문에 당연히 로컬 오류 처리가 트리거되지 않을 가능성이 높습니다 .
여기 에서 작동하는 데모 플 런커를 실행 하십시오 .
답변
최신 RxJs 기능 (v.6)으로 HttpInterceptor를 사용하는 것에 대한 acdcjunior 의 답변을 업데이트하겠습니다 .
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;
})
);
}
}
답변
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 스크립트를 사용하는 경우 해당 유형 부분을 사용하십시오. 당신은 언어의 가장 큰 장점 중 하나를 잃게됩니다 : 당신이 다루는 가치의 유형을 아는 것입니다.
내 생각에 각도 서비스의 좋은 예를 원한다면 다음 요점을 살펴보십시오 .
답변
상당히 간단합니다 (이전 API로 수행 한 방법에 비해).
Angular 공식 가이드의 출처 (복사 및 붙여 넣기)
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.');
}
}
자세한 내용은 Angular Guide for Http를 참조하십시오.
답변
Angular 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
})
서비스를 어떻게 사용 하느냐에 따라 크게 다르지만 이것이 기본적인 경우입니다.