Wie kann ich eine Ausnahme von http.request () korrekt abfangen?

132

Teil meines Codes:

import {Injectable} from 'angular2/core';
import {Http, Headers, Request, Response} from 'angular2/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';

@Injectable()
export class myClass {

  constructor(protected http: Http) {}

  public myMethod() {
    let request = new Request({
      method: "GET",
      url: "http://my_url"
    });

    return this.http.request(request)
      .map(res => res.json())
      .catch(this.handleError); // Trouble line. 
                                // Without this line code works perfectly.
  }

  public handleError(error: Response) {
    console.error(error);
    return Observable.throw(error.json().error || 'Server error');
  }

}

myMethod() erzeugt eine Ausnahme in der Konsole des Browsers:

ORIGINAL AUSNAHME: TypeError: this.http.request (...). Map (...). Catch ist keine Funktion

mnv
quelle

Antworten:

213

Vielleicht können Sie versuchen, dies in Ihren Importen hinzuzufügen:

import 'rxjs/add/operator/catch';

Sie können auch tun:

return this.http.request(request)
  .map(res => res.json())
  .subscribe(
    data => console.log(data),
    err => console.log(err),
    () => console.log('yay')
  );

Per Kommentare:

AUSNAHME: TypeError: Observable_1.Observable.throw ist keine Funktion

In ähnlicher Weise können Sie dafür Folgendes verwenden:

import 'rxjs/add/observable/throw';
acdcjunior
quelle
2
Danke für die Hilfe, es funktioniert. Danach habe ich das gleiche Problem mit der throw()Funktion. Ich habe import 'rxjs/Rx';stattdessen diese Zeile hinzugefügt . Jetzt funktionieren alle Bediener ordnungsgemäß.
Mnv
Haben Sie einen Fehler simuliert, um festzustellen, ob das .catchwirklich funktioniert? Das .subscribe() funktioniert sicher.
Acdcjunior
1
Ja, das zweite Problem war EXCEPTION: TypeError: Observable_1.Observable.throw is not a function. Es kann mit der Antwort @MattScarpino oder in der Manier von diesem Plunker aus behoben werden, wie ich oben sagte: angle.io/resources/live-examples/server-communication/ts/…
mnv
16
Importieren Sie einfach auch werfen: import 'rxjs/add/observable/throw';und importieren Sie nicht alles, es ist zu groß.
dfsq
Tolle Lösung, sehr hilfreich, ich könnte hinzufügen, dass der (err) vom Typ Antwort ist
Mohammed Suez
76

Neuer Dienst aktualisiert, um HttpClientModule und RxJS v5.5.x zu verwenden :

import { Injectable }                    from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable }                    from 'rxjs/Observable';
import { catchError, tap }               from 'rxjs/operators';
import { SomeClassOrInterface}           from './interfaces';
import 'rxjs/add/observable/throw';

@Injectable() 
export class MyService {
    url = 'http://my_url';
    constructor(private _http:HttpClient) {}
    private handleError(operation: String) {
        return (err: any) => {
            let errMsg = `error in ${operation}() retrieving ${this.url}`;
            console.log(`${errMsg}:`, err)
            if(err instanceof HttpErrorResponse) {
                // you could extract more info about the error if you want, e.g.:
                console.log(`status: ${err.status}, ${err.statusText}`);
                // errMsg = ...
            }
            return Observable.throw(errMsg);
        }
    }
    // public API
    public getData() : Observable<SomeClassOrInterface> {
        // HttpClient.get() returns the body of the response as an untyped JSON object.
        // We specify the type as SomeClassOrInterfaceto get a typed result.
        return this._http.get<SomeClassOrInterface>(this.url)
            .pipe(
                tap(data => console.log('server data:', data)), 
                catchError(this.handleError('getData'))
            );
    }

Alter Dienst, der das veraltete HttpModule verwendet:

import {Injectable}              from 'angular2/core';
import {Http, Response, Request} from 'angular2/http';
import {Observable}              from 'rxjs/Observable';
import 'rxjs/add/observable/throw';
//import 'rxjs/Rx';  // use this line if you want to be lazy, otherwise:
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';  // debug
import 'rxjs/add/operator/catch';

@Injectable()
export class MyService {
    constructor(private _http:Http) {}
    private _serverError(err: any) {
        console.log('sever error:', err);  // debug
        if(err instanceof Response) {
          return Observable.throw(err.json().error || 'backend server error');
          // if you're using lite-server, use the following line
          // instead of the line above:
          //return Observable.throw(err.text() || 'backend server error');
        }
        return Observable.throw(err || 'backend server error');
    }
    private _request = new Request({
        method: "GET",
        // change url to "./data/data.junk" to generate an error
        url: "./data/data.json"
    });
    // public API
    public getData() {
        return this._http.request(this._request)
          // modify file data.json to contain invalid JSON to have .json() raise an error
          .map(res => res.json())  // could raise an error if invalid JSON
          .do(data => console.log('server data:', data))  // debug
          .catch(this._serverError);
    }
}

Ich benutze .do()( jetzt.tap() ) zum Debuggen.

Wenn ein Serverfehler auftritt, wird der bodyvonResponse Objekt , das ich vom Server erhalte Ich verwende (lite-Server) enthalten nur Text, daher auch den Grund , warum ich verwende err.text()oben statt err.json().error. Möglicherweise müssen Sie diese Zeile für Ihren Server anpassen.

Wenn res.json()ein Fehler ausgelöst wird, weil die JSON-Daten nicht analysiert werden konnten,_serverError wird kein ResponseObjekt abgerufen, daher der Grund für die instanceofÜberprüfung.

In diesem plunker, ändernurl sich, um ./data/data.junkeinen Fehler zu erzeugen.


Benutzer beider Dienste sollten über Code verfügen, der den Fehler behandeln kann:

@Component({
    selector: 'my-app',
    template: '<div>{{data}}</div> 
       <div>{{errorMsg}}</div>`
})
export class AppComponent {
    errorMsg: string;
    constructor(private _myService: MyService ) {}
    ngOnInit() {
        this._myService.getData()
            .subscribe(
                data => this.data = data,
                err  => this.errorMsg = <any>err
            );
    }
}
Mark Rajcok
quelle
4

Es gibt verschiedene Möglichkeiten, dies zu tun. Beide sind sehr einfach. Jedes der Beispiele funktioniert hervorragend. Sie können es in Ihr Projekt kopieren und testen.

Die erste Methode ist vorzuziehen, die zweite ist etwas veraltet, aber bisher funktioniert sie auch.

1) Lösung 1

// File - app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';

import { AppComponent } from './app.component';
import { ProductService } from './product.service';
import { ProductModule } from './product.module';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule
  ],
  providers: [ProductService, ProductModule],
  bootstrap: [AppComponent]
})
export class AppModule { }



// File - product.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

// Importing rxjs
import 'rxjs/Rx';
import { Observable } from 'rxjs/Rx';
import { catchError, tap } from 'rxjs/operators'; // Important! Be sure to connect operators

// There may be your any object. For example, we will have a product object
import { ProductModule } from './product.module';

@Injectable()
export class ProductService{
    // Initialize the properties.
    constructor(private http: HttpClient, private product: ProductModule){}

    // If there are no errors, then the object will be returned with the product data.
    // And if there are errors, we will get into catchError and catch them.
    getProducts(): Observable<ProductModule[]>{
        const url = 'YOUR URL HERE';
        return this.http.get<ProductModule[]>(url).pipe(
            tap((data: any) => {
                console.log(data);
            }),
            catchError((err) => {
                throw 'Error in source. Details: ' + err; // Use console.log(err) for detail
            })
        );
    }
}

2) Lösung 2. Es ist ein alter Weg, funktioniert aber immer noch.

// File - app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpModule } from '@angular/http';

import { AppComponent } from './app.component';
import { ProductService } from './product.service';
import { ProductModule } from './product.module';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpModule
  ],
  providers: [ProductService, ProductModule],
  bootstrap: [AppComponent]
})
export class AppModule { }



// File - product.service.ts
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';

// Importing rxjs
import 'rxjs/Rx';
import { Observable } from 'rxjs/Rx';

@Injectable()
export class ProductService{
    // Initialize the properties.
    constructor(private http: Http){}

    // If there are no errors, then the object will be returned with the product data.
    // And if there are errors, we will to into catch section and catch error.
    getProducts(){
        const url = '';
        return this.http.get(url).map(
            (response: Response) => {
                const data = response.json();
                console.log(data);
                return data;
            }
        ).catch(
            (error: Response) => {
                console.log(error);
                return Observable.throw(error);
            }
        );
    }
}
Victor Isaikin
quelle
-1

Die RxJS-Funktionen müssen speziell importiert werden. Eine einfache Möglichkeit, dies zu tun, besteht darin, alle Funktionen mit zu importierenimport * as Rx from "rxjs/Rx"

Stellen Sie dann sicher, dass Sie auf die ObservableKlasse als zugreifen Rx.Observable.

MatthewScarpino
quelle
15
Rxjs ist eine sehr große Datei. Wenn Sie alle Funktionen importieren, erhöht sich Ihre Ladezeit
Soumya Gangamwar
Sie sollten nicht einfach alles aus Rxjs importieren, wenn Sie nur einen oder zwei Operatoren benötigen.
marcel-k
-4

in der neuesten Version von angle4 verwenden

import { Observable } from 'rxjs/Rx'

Es werden alle erforderlichen Dinge importiert.

Munish Sharma
quelle
20
Tun Sie dies nicht, es werden alle Rxjs importiert.
marcel-k
Dies führt zu einer größeren Bündelgröße!
Tushar Walzade