Tengo un servicio de datos que se parece a esto:
lenguaje: typescript -->
@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)
}
}
Si obtengo un error HTTP (es decir, 404), obtengo un mensaje de consola desagradable: ERROR Error: Uncaught (in promise): [object Object] from core.es5.js ¿Cómo lo manejo en mi caso?
Tienes algunas opciones, dependiendo de tus necesidades. Si quieres manejar los errores en base a cada solicitud, añade un catch
a tu solicitud. Si quieres añadir una solución global, utiliza HttpInterceptor
.
Abra aquí el plunker de demostración de trabajo para las soluciones que se indican a continuación.
En el caso más simple, sólo tendrá que añadir un .catch()
o un .subscribe()
, como:
.catch()
o .subscribe()
, como
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)
);
Pero hay más detalles al respecto, véase más abajo.
Si necesitas manejar errores en un solo lugar, puedes usar catch
y devolver un valor por defecto (o una respuesta vacía) en lugar de fallar completamente. Tampoco necesitas el .map
sólo para hacer el casting, puedes usar una función genérica. Fuente: Angular.io - Obteniendo detalles del error.
Así, un método genérico .get()
, sería como:
lenguaje: typescript -->
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>();
});
}
}
El manejo del error le permitirá a su aplicación continuar incluso cuando el servicio en la URL esté en malas condiciones.
Esta solución por petición es buena sobre todo cuando se quiere devolver una respuesta por defecto específica a cada método. Pero si sólo te importa la visualización de errores (o tienes una respuesta global por defecto), la mejor solución es utilizar un interceptor, como se describe a continuación.
Ejecute la demostración de trabajo de plunker aquí.
Una vez más, la guía de Angular.io muestra:
Una característica importante de @angular/common/http
es la interceptación, la capacidad de declarar interceptores que se sitúan entre tu aplicación y el backend. Cuando tu aplicación hace una petición, los interceptores la transforman antes de enviarla al servidor, y los interceptores pueden transformar la respuesta en su camino de vuelta antes de que tu aplicación la vea. Esto es útil para todo, desde la autenticación hasta el registro.
Que, por supuesto, puede ser utilizado para manejar los errores de una manera muy simple (demo plunker aquí):
lenguaje: typescript -->
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>>();
});
}
}
Proporcionando su interceptor: Simplemente declarando el HttpErrorInterceptor
arriba no hace que su aplicación lo use. Necesitas conectarlo en el módulo de tu aplicación proporcionándolo como un interceptor, como sigue:
lenguaje: typescript -->
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 {}
Nota: Si tienes tanto un interceptor de errores como un manejo de errores local, naturalmente, es probable que nunca se active ningún manejo de errores local, ya que el error siempre será manejado por el interceptor antes de que llegue al manejo de errores local.
Ejecute la demostración de trabajo aquí.
Siguiendo la respuesta de @acdcjunior, así es como lo implementé
servicio:
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();
}
llamador:
this.dataService.get(baseUrl, params)
.then((object) => {
if(object['name'] === 'HttpErrorResponse') {
this.error = true;
//or any handle
} else {
this.myObj = object as MyClass
}
});