Jak poprawnie wyłapać wyjątek z http.request ()?

134

Część mojego kodu:

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() tworzy wyjątek w konsoli przeglądarki:

ORYGINALNY WYJĄTEK: TypeError: this.http.request (...). Map (...). Catch nie jest funkcją

mnv
źródło

Odpowiedzi:

220

Być może możesz spróbować dodać to w swoim imporcie:

import 'rxjs/add/operator/catch';

Możesz też:

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

Za komentarze:

WYJĄTEK: TypeError: Observable_1.Observable.throw nie jest funkcją

Podobnie możesz użyć do tego:

import 'rxjs/add/observable/throw';
acdcjunior
źródło
2
Dziękuję za pomoc, działa. Potem mam ten sam problem z throw()funkcją. import 'rxjs/Rx';Zamiast tego dodałem tę linię . Teraz wszystkie operatory działają poprawnie.
mnv
Czy zasymulowałeś błąd, aby sprawdzić, czy .catchnaprawdę działa? To .subscribe() na pewno działa.
acdcjunior
1
Tak, drugi problem był EXCEPTION: TypeError: Observable_1.Observable.throw is not a function. Można to naprawić za pomocą odpowiedzi @MattScarpino lub w inny sposób z tego plunkera, jak powiedziałem powyżej: angular.io/resources/live-examples/server-communication/ts/ ...
mnv
16
Po prostu importuj też rzut: import 'rxjs/add/observable/throw';i nie importuj wszystkiego, jest zbyt duży.
dfsq
Świetne rozwiązanie, bardzo pomocne, dodam, że (błąd) jest typu Response
Mohammed Suez
77

Nowa usługa została zaktualizowana do korzystania z HttpClientModule i RxJS v5.5.x :

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

Stara usługa, która używa przestarzałego HttpModule:

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

Używam .do()( teraz.tap() ) do debugowania.

Kiedy pojawia się błąd serwera The bodytego Responseobiektu, które otrzymuję z serwera używam (Lite-serwer) zawiera tylko tekst, stąd powód używam err.text()powyżej zamiast err.json().error. Może być konieczne dostosowanie tej linii do serwera.

Jeśli res.json()wywołuje błąd, ponieważ nie może przeanalizować danych JSON, _serverErrornie otrzyma Responseobiektu, stąd przyczyna instanceofsprawdzenia.

W tym celu plunkerzmień urlna, ./data/data.junkaby wygenerować błąd.


Użytkownicy obu usług powinni mieć kod, który może obsłużyć błąd:

@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
źródło
4

Można to zrobić na kilka sposobów. Obie są bardzo proste. Każdy z przykładów działa świetnie. Możesz skopiować go do swojego projektu i przetestować.

Pierwsza metoda jest lepsza, druga jest nieco przestarzała, ale na razie też działa.

1) Rozwiązanie 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) Rozwiązanie 2. To stary sposób, ale nadal działa.

// 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
źródło
-1

Funkcje RxJS muszą zostać specjalnie zaimportowane. Prostym sposobem na to jest zaimportowanie wszystkich jego funkcji za pomocąimport * as Rx from "rxjs/Rx"

Następnie upewnij się, że masz dostęp do Observableklasy jako Rx.Observable.

MatthewScarpino
źródło
15
Rxjs to bardzo duży plik, jeśli zaimportujesz wszystkie jego funkcje, zwiększy to czas ładowania
Soumya Gangamwar
Nie powinieneś po prostu importować wszystkiego z Rxjs, jeśli potrzebujesz tylko jednego lub dwóch operatorów.
marcel-k
-4

w najnowszej wersji wykorzystania angular4

import { Observable } from 'rxjs/Rx'

zaimportuje wszystkie wymagane rzeczy.

Munish Sharma
źródło
20
Nie rób tego, zaimportuje wszystkie pliki Rxjs.
marcel-k
A to spowoduje zwiększenie rozmiaru pakietu!
Tushar Walzade,