Centralized HTTP error handling in Angular 4

I want to tell the user if the HTTP request failed, without having to write additional code for each HTTP request.

I had a working prototype for angular 2:

@Injectable() export class MyErrorHandler extends ErrorHandler { constructor() { super(false); } handleError(error: any) { this.tellUser(error); super.handleError(error); } tellUser(error: any) { // dig for the root cause if (error.rejection) { error = error.rejection; } if (error instanceof ViewWrappedError) { error = error.originalError; } let message; if (error instanceof Response) { // TODO: localize and display nicely const messages = { 0: "Could not connect to server", } message = messages[error.status] || ("The server reported a system error (HTTP " + error.status + ")"); } else { message = "A system error occurred."; } console.warn(message); } } 

but ViewWrappedError has been replaced by angular 4 with

 export function viewWrappedDebugError(err: any, context: DebugContext): Error { if (!(err instanceof Error)) { // errors that are not Error instances don't have a stack, // so it is ok to wrap them into a new Error object... err = new Error(err.toString()); } _addDebugContext(err, context); return err; } 

Which makes calling the toString in HttpResponse more difficult to request a status code ...

I would expect angular to provide a public, well-supported API for centralized error handling. Is there a way to centrally handle HTTP error handling other than handling error messages?

Update: I would prefer the component to easily override the centralized error handling.

+5
source share
2 answers

Another option is simply to have a service that delegates the Http service when adding any custom error handling, debug logic, extra headers, etc. that you will need to interact with your API.

This service becomes your centralized point for interacting with everything related to Http, and therefore you can centralize error handling right there. In any project that I am developing, I ALWAYS create such a class, because almost every single API you interact with has some common configuration that should happen with every request (even if this configuration is no less than specifying a base url for use).

An example of such a class:

 export class ApiGateway { baseURL = "https://myapi.com"; // or sometimes pulled from another file constructor(private http: Http) { } get(path, params) { showLoadingIndicator(); let headers = this.createMySpecialHeaders(); let options = { headers: headers } // and whatever else you need to generalize let fullUrl = this.baseUrl + path + '?' + this.urlEncode(params)`; return this.get(path, params, options) .do(() => hideLoadingIndicator()) .map(res => res.json()) .catch(err => { hideLoadingIndicator(); // show error message or whatever the app does with API errors etc // sometimes rethrow the error, depending on the use case }) } } 

For me, these are the basic principles of OOP - you exchange your interactions with things outside your control in the adapter class to provide you some protection against external changes and change the API of this external thing to something that you prefer, if necessary.

With such a class in place, if, for example, you upgrade to Angular 4 and change the way you get errors, you only have one place to change to handle the new error technique.

+1
source

I centralize this logic inside the BaseService and then inherit every service from it. Angular 2 does not provide Http Interceptors as a previous version, which makes it difficult to work with similar material.

 import { Injectable } from '@angular/core'; import { Response } from '@angular/http'; import { Observable } from 'rxjs/Observable'; import { NotificationService } from '********'; @Injectable() export class BaseService { protected url: string; protected http: Http; constructor(http: Http, protected notification: NotificationService) { this.http = http; } protected extractData(res: Response) { // // Depende do formato da response, algumas APIs retornam um objeto data e dentro deste o conteudo propriamente dito // Em minhas APIs sempre retorno diretamente o objeto que desejo // return res.json(); } protected handleError(error: Response | any) { let erros: string[] = []; switch (error.status) { case 400: let res = error.json(); // // Depende do formato, minhas APIs sempre retornar um objeto com array errors quando tenho 400 // for (let index = 0; index < res.errors.length; index++) { let msg = res.errors[index]; erros.push(msg); }; this.notification.showMultipleWarn(erros); break; case 404: this.notification.showWarning('O recurso requisitado não existe.'); break; case 406: case 409: case 500: this.notification.showError('Ocorreu um erro inesperado.'); break; default: this.notification.showError('Ocorreu um erro inesperado.'); break; } return Observable.throw(new Error(error.status)); } } 

Here's my blog post explaining some way to handle this in less detail: Receitas Angular2: Interceptando erros HTTP de forma Global

The message is written in Portuguese, but the code may give you some suggestions. I use this approach in some large projects right now and it works great.

0
source

Source: https://habr.com/ru/post/1266092/


All Articles