Saltar al contenido

Detectando errores en Angular HttpClient

Posterior a de una extensa búsqueda de información hemos podido resolver este atascamiento que tienen algunos usuarios. Te dejamos la respuesta y nuestro objetivo es resultarte de gran apoyo.

Solución:

Tienes algunas opciones, según tus necesidades. Si desea manejar los errores por solicitud, agregue un catch a su solicitud. Si desea agregar una solución global, use HttpInterceptor.

Abierto aquí el plunker de demostración de trabajo para las soluciones a continuación.

tl; dr

En el caso más simple, solo necesitará agregar un .catch() o un .subscribe(), igual que:

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 sobre esto, ver más abajo.

Solución del método (local): registrar el error y devolver la respuesta de reserva

Si necesita manejar errores en un solo lugar, puede usar catch y devuelve un valor predeterminado (o respuesta vacía) en lugar de fallar por completo. Tampoco necesitas el .map solo para transmitir, puede usar una función genérica. Fuente: Angular.io – Obteniendo detalles del error.

Entonces, un genérico .get() método, sería como:

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 , making the method generic
    get(url, params): Observable 
      return this.httpClient
          .get(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(my: "default value...");
            // or simply an empty observable
            return Observable.empty();
          );
     

Manejar el error permitirá que la aplicación continúe incluso cuando el servicio en la URL esté en malas condiciones.

Esta solución por solicitud es buena principalmente cuando desea devolver una respuesta predeterminada específica a cada método. Pero si solo le importa la visualización de errores (o tiene una respuesta predeterminada global), la mejor solución es usar un interceptor, como se describe a continuación.

Ejecutar el plunker de demostración de trabajo aquí.

Uso avanzado: interceptar todas las solicitudes o respuestas

Una vez más, la guía Angular.io muestra:

Una característica importante de @angular/common/http es la interceptación, la capacidad de declarar interceptores que se encuentran entre su aplicación y el backend. Cuando su aplicación realiza una solicitud, los interceptores la transforman antes de enviarla al servidor, y los interceptores pueden transformar la respuesta en su camino de regreso antes de que su aplicación la vea. Esto es útil para todo, desde la autenticación hasta el registro.

Que, por supuesto, se puede utilizar para manejar errores de una forma muy sencilla (demo plunker aquí):

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, next: HttpHandler): Observable> 
    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>();
      );
  

Proporcionando su interceptor: Simplemente declarando el HttpErrorInterceptor anterior no hace que su aplicación lo use. Debe conectarlo en el módulo de su aplicación proporcionándolo como un interceptor, de la siguiente manera:

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 usted tiene ambos un interceptor de errores y algún manejo local de errores, naturalmente, es probable que nunca se active el manejo local de errores, ya que el error siempre será manejado por el interceptor antes de llega al manejo de errores local.

Ejecutar el plunker de demostración de trabajo aquí.

Permítanme actualizar la respuesta de acdcjunior sobre el uso de HttpInterceptor con las últimas funciones de 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, next: HttpHandler): Observable> 

    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;
      )
    );
  

Con la llegada del HTTPClient API, no solo fue la Http API reemplazada, pero se agregó una nueva, la HttpInterceptor API.

AFAIK uno de sus objetivos es agregar un comportamiento predeterminado a todas las solicitudes salientes HTTP y respuestas entrantes.

Asumiendo que quieres agregar un comportamiento de manejo de errores predeterminado, agregando .catch() a todos sus posibles métodos http.get / post / etc es ridículamente difícil de mantener.

Esto podría hacerse de la siguiente manera como ejemplo usando un 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, next: HttpHandler): Observable> 
        return next.handle(req)
            .catch(errorResponse => 
                let errMsg: string;
                if (errorResponse instanceof HttpErrorResponse)  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 

Alguna información adicional para OP: llamar a http.get / post / etc sin un tipo fuerte no es un uso óptimo de la API. Su servicio debería verse así:

// 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 

        return this.http.get(this.baseUrl + url, params);
    

    post(url, body): Observable 
        return this.http.post(this.baseUrl + url, body);
    

    patch(url, body): Observable 
        return this.http.patch(this.baseUrl + url, body);
    

Regresando Promises de sus métodos de servicio en lugar de Observables es otra mala decisión.

Y un consejo adicional: si está utilizando ESCRIBEscript, luego comience a usar la parte de tipo del mismo. Pierdes una de las mayores ventajas del lenguaje: conocer el tipo de valor con el que estás lidiando.

Si desea, en mi opinión, un buen ejemplo de un servicio angular, eche un vistazo a la siguiente esencia.

Valoraciones y reseñas

Si conservas algún recelo y forma de limar nuestro crónica te proponemos realizar una referencia y con placer lo leeremos.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *