Angular, HTTP Error Handler

Eine Rest-Schnittstelle über den von Angular bereitgestellten HTTP-Service an zu binden ist nicht schwer. Dank Observables ist auch die Fehlerbehandlung kein großer Aufwand. Trotzdem muss diese Behandlung für jede Kommunikation deklariert werden was zu lästigem Schreibaufwand führt und die Anwendung auch unübersichtlich macht. Wie sieht eine Möglichkeit aus, generisch auf HTTP Fehler zu reagieren, ohne diese Behandlung bei jedem Aufruf zu implementieren? Werfen wir einen Blick darauf.

app

Um zum Beispiel mit Rest-Schnittstellen zu kommunizieren verwenden wir in Angular den bereitgestellten HTTP Service der uns diverse Methoden zur Verfügung stellt die verschiedenen Request-Typen zu verwenden. Ein einfache Beispiel und dessen Verwendung könnte so aussehen:

jsonplace-holder.service.ts

@Injectable()
export class JSONPlaceHolderService {

  constructor(private http: Http, @Inject(SERVICE_BASE_URL) private baseurl:string) { }

  ladeUserDaten(userId: number): Observable<any> {
    return this.http.get( this.baseurl + 'users/' + userId).map(r => r.json());
  }

}

app.component.ts

  ladeDaten() {
    this.service.ladeUserDaten(5).subscribe(
      u => this.userDaten = u,
      error => console.log('Fehler: ' + error)
   );
  }

(die Fehlerbehandlung durch eine Konsolen-Ausgabe zu implementieren ist natürlich keine gute Idee. In aller Regel werden wir einen zentralen Service verwenden der sich darum kümmert das die Ausgaben an den Benutzer weiter gegeben werden)

Die Behandlung der Fehler, egal in welcher Form sie nun implementiert ist, müsste nun bei jedem Methodenaufruf verwendet werden. Selbst dann wenn wir lediglich eine ganze allgmeine Meldung ausgeben wollen.

Die eigene ‚http‘ Implementierung

Der Titel klingt umfassender als es wirklich ist. Eigentlich erweitern wir den bestehenden HTTP-Service lediglich um eine einheitliche Fehlerbehandlung.

Dazu überschreiben wir die Request-Methode, leiten den eigentlichen Aufruf an die originale Implementierung weiter und registrieren eine Methode („catch“) die sich um unsere Fehlerbehandlung kümmern soll. In diesem Fall leitet der HTTP-Interceptor diese Fehlermeldung an einen separaten Service weiter um den User diese Information an zu zeigen.

jsonplace-holder.service.ts

// operators
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/map';

@Injectable()
export class HttpInterceptor extends Http {

    constructor(backend: XHRBackend,options: RequestOptions, private errorService:ErrorHandleService) {
        super(backend, options)
    }

    public request(url: string|Request, options?: RequestOptionsArgs): Observable<Response> {
        return super.request(url, options)
            .catch(this.handleError)
    }

    public handleError = (error: Response) => {
        this.errorService.addError(error.toString());
        return Observable.throw(error)
    }
}

Soweit so gut. Nun müssen wir lediglich dafür Sorge tragen das bei der Injizierung des HTTP-Services (wie oben zu sehen) unsere eigene Implementierung herangezogen wird und nicht die von Angular selbst. Dazu implementieren wir einen entsprechenden Provider in unserem Modul. Dieser Provider ist hier im Beispiel eine Factory-Methode, damit wir andere Services die ebenfalls per Depedency Injektion bereit gestellt werden an unsere Implementierung übergeben können:

app.module.ts

  providers: [
   ...
    ErrorHandleService,
    {
      provide: Http,
      deps: [XHRBackend, RequestOptions, ErrorHandleService],
      useFactory: (backend: XHRBackend, defaultOptions: RequestOptions, service: ErrorHandleService) => new HttpInterceptor(backend, defaultOptions, service)
    }
  ],

Nachricht an den User

Zur Vollständigkeit noch eine kurze Zusammenfassung wie die Benachrichtigung an den Benutzer erfolgt. Wie oben zu sehen verwenden wir einen Service der die Fehler entgegen nimmt. Dieser Service ist sehr überschaubar und hält lediglich ein „Subject“ (rxjs-Packet) bereit an dem sich andere Komponenten registrieren können. In unserem Mini-Beispiel erfolgt die Registrierung direkt in der Hauptkomponenten die bei neuen Nachrichten diese in einer Variablen ablegt, auf dessen Basis eine Meldung im Frontend angezeigt wird. Zusätzlich blenden wir die entsprechenden Fehlermeldungen noch nach einigen Sekunden aus.

error-handler.service.ts

@Injectable()
export class ErrorHandleService {

  public messages = new Subject<string>();

  addError(error: string) {
    this.messages.next(error);
    throw error;
  }
}

app.component.ts

    errorService.messages.subscribe(e => {
      this.globalErrorMessage = e;
      setTimeout(() => this.globalErrorMessage = null, 5000);
    });

app.component.html

<div *ngIf="globalErrorMessage">
<div class="alert alert-danger">
            {{globalErrorMessage}}</div>
</div>

 

Wie immer. Alles. Live. In Farbe. Bei Github

Advertisements

Über Dominik Mathmann
Dominik Mathmann arbeitet als Berater, Entwickler und Trainer bei der GEDOPLAN GmbH. Auf Basis seiner langjährigen Erfahrung in der Implementierung von Java-EE-Anwendungen leitet er Seminare, hält Vorträge und unterstützt Kunden bei der Konzeption und Realisierung von Webanwendungen vom Backend bis zum Frontend. Sein derzeitiger Schwerpunkt liegt dabei auf der Entwicklung von JSF-Anwendungen. Er sieht die stärker werdende Position von JavaScript-Frameworks jedoch positiv und beschäftigt sich darüber hinaus mit Webframeworks wie AngularJS.

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

%d Bloggern gefällt das: