2017-03-29 17 views
5

Chcę poinformować użytkownika, jeśli żądanie HTTP nie powiedzie się, bez konieczności pisania dodatkowego kodu dla każdego żądania HTTP.Scentralizowana obsługa błędów HTTP w Angular 4

miałem działający prototyp dla kątowego 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); 
    } 
} 

ale ViewWrappedError został zastąpiony Kątowymi 4 przez

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

, które ze względu na wywoływanie toString na httpResponse czyni go trudno przetestować kod statusu ...

Spodziewałbym się, że dostarczę publiczny, dobrze obsługiwany interfejs API do scentralizowanej obsługi błędów. Czy naprawdę nie można centralnie obsługiwać błędów HTTP innych niż analizowanie komunikatów o błędach?

Aktualizacja: Wolałbym, jeśli komponent mógł z łatwością zastąpić scentralizowaną obsługę błędów.

Odpowiedz

0

Centralizuję tego rodzaju logikę wewnątrz BaseService, a następnie dziedziczę każdą usługę z niego. Angular 2 nie zapewnia Http Interceptors jako poprzedniej wersji, co sprawia, że ​​tego rodzaju rzeczy są trudne do zniesienia.

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

Oto mój blogu wyjaśniając jakiś sposób, by się tym zająć mniej --long: Receitas Angular2: Interceptando erros HTTP de forma Global

post jest napisany w języku portugalskim, ale kod może dać pewne wskazówki. Używam tego podejścia w niektórych dużych projektach już teraz i działa dobrze.

1

Inną opcją jest po prostu posiadanie usługi, która deleguje do usługi Http podczas dodawania niestandardowej obsługi błędów, logiki debugowania, dodatkowych nagłówków itp., Które wymagają interakcji z interfejsem API.

Usługa ta staje się scentralizowanym punktem do interakcji z czymkolwiek związanym z Http, dzięki czemu można centralizować obsługę błędów właśnie tutaj. W każdym rozwijanym przeze mnie projekcie ZAWSZE tworzę taką klasę, ponieważ praktycznie każdy pojedynczy interfejs API, z którym wchodzisz, ma pewną wspólną konfigurację, która musi się zdarzyć przy każdym żądaniu (nawet jeśli ta konfiguracja jest tak mała, jak określenie podstawowego adresu URL do użycia).

Przykład takiej klasy:

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

Dla mnie jest to podstawowe zasady OOP - owinąć swoje interakcje z rzeczy poza kontrolą w klasie adaptera, aby umożliwić pewną ochronę przed zmianami zewnętrznymi i zmienić API tej rzeczy zewnętrznej na coś, co lubisz, jeśli to konieczne.

Po wprowadzeniu takiej klasy, na przykład w przypadku aktualizacji do wersji Angular 4 i zmiany sposobu odbierania błędów, można zmienić tylko jedno miejsce, aby obsłużyć nową technikę błędu.