Comment attraper correctement l'exception de http.request ()?

132

Une partie de mon code:

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() produit une exception dans la console du navigateur:

EXCEPTION ORIGINALE: TypeError: this.http.request (...). Map (...). Catch n'est pas une fonction

mnv
la source

Réponses:

213

Vous pouvez peut-être essayer d'ajouter ceci dans vos importations:

import 'rxjs/add/operator/catch';

Vous pouvez également faire:

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

Par commentaires:

EXCEPTION: TypeError: Observable_1.Observable.throw n'est pas une fonction

De même, pour cela, vous pouvez utiliser:

import 'rxjs/add/observable/throw';
acdcjunior
la source
2
Merci pour l'aide, ça marche. Après cela, j'ai le même problème avec la throw()fonction. J'ai ajouté cette ligne à la import 'rxjs/Rx';place. Désormais, tous les opérateurs fonctionnent correctement.
mnv
Avez-vous simulé une erreur pour voir si .catchcela fonctionne vraiment? Cela .subscribe() fonctionne à coup sûr.
acdcjunior
1
Oui, le deuxième problème était EXCEPTION: TypeError: Observable_1.Observable.throw is not a function . Il peut être corrigé avec la réponse @MattScarpino ou dans la manière de ce plunker comme je l'ai dit ci-dessus: angular.io/resources/live-examples/server-communication/ts/…
mnv
16
Importez simplement le jet: import 'rxjs/add/observable/throw';et n'importez pas tout, c'est trop énorme.
dfsq
Excellente solution, très utile, je pourrais ajouter que (err) est de type Réponse
Mohammed Suez
77

Nouveau service mis à jour pour utiliser HttpClientModule et 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'))
            );
    }

Ancien service, qui utilise le HttpModule obsolète:

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

J'utilise .do()( maintenant.tap() ) pour le débogage.

Quand il y a une erreur de serveur, le bodyde l' Responseobjet que je reçois du serveur j'utilise (lite-serveur) contient seulement du texte, d' où la raison pour laquelle je l' utilise err.text()ci - dessus plutôt que err.json().error. Vous devrez peut-être ajuster cette ligne pour votre serveur.

Si res.json()génère une erreur car il n'a pas pu analyser les données JSON, _serverErrorn'obtiendra pas d' Responseobjet, d'où la raison de la instanceofvérification.

Dans ce plunker , changez urlen ./data/data.junkpour générer une erreur.


Les utilisateurs de l'un ou l'autre service doivent avoir un code capable de gérer l'erreur:

@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
la source
4

Il y a plusieurs moyens de le faire. Les deux sont très simples. Chacun des exemples fonctionne très bien. Vous pouvez le copier dans votre projet et le tester.

La première méthode est préférable, la seconde est un peu dépassée, mais jusqu'à présent, elle fonctionne aussi.

1) Solution 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) Solution 2. Elle est ancienne mais fonctionne toujours.

// 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
la source
-1

Les fonctions RxJS doivent être spécifiquement importées. Un moyen simple de le faire est d'importer toutes ses fonctionnalités avecimport * as Rx from "rxjs/Rx"

Assurez-vous ensuite d'accéder à la Observableclasse en tant que Rx.Observable.

MatthieuScarpino
la source
15
Rxjs est un très gros fichier, si vous importez toutes ses fonctionnalités, votre temps de chargement
augmentera
Vous ne devez pas simplement tout importer de Rxjs si vous n'avez besoin que d'un ou deux opérateurs.
marcel-k
-4

dans la dernière version d'angular4 use

import { Observable } from 'rxjs/Rx'

il importera toutes les choses requises.

Munish Sharma
la source
20
Ne faites pas cela, cela importera tous les Rxjs.
marcel-k
Et cela entraînera une augmentation de la taille du paquet!
Tushar Walzade