Bagaimana cara menangkap pengecualian dengan benar dari http.request ()?

132

Bagian dari kode saya:

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() menghasilkan pengecualian di konsol browser:

PENGECUALIAN ASLI: TypeError: this.http.request (...). Map (...). Catch bukan fungsi

mnv
sumber

Jawaban:

213

Mungkin Anda dapat mencoba menambahkan ini di impor Anda:

import 'rxjs/add/operator/catch';

Anda juga dapat melakukan:

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

Per komentar:

PENGECUALIAN: TypeError: Observable_1.Observable.throw bukan fungsi

Demikian pula, untuk itu, Anda dapat menggunakan:

import 'rxjs/add/observable/throw';
acdcjunior
sumber
2
Terima kasih atas bantuannya, ini berhasil. Setelah itu saya memiliki masalah yang sama dengan throw()fungsi. Saya menambahkan baris ini import 'rxjs/Rx';sebagai gantinya. Sekarang semua operator bekerja dengan baik.
mnv
Apakah Anda mensimulasikan kesalahan untuk melihat apakah itu .catchbenar - benar berfungsi? Itu .subscribe() pasti berhasil.
acdcjunior
1
Ya, masalah kedua adalah EXCEPTION: TypeError: Observable_1.Observable.throw is not a function. Ini mungkin diperbaiki dengan jawaban @MattScarpino atau maner dari plunker ini seperti yang saya katakan di atas: angular.io/resources/live-examples/server-communication/ts/...
MNV
16
Impor juga lemparan: import 'rxjs/add/observable/throw';dan jangan impor semuanya, itu terlalu besar.
dfsq
Solusi hebat ,, sangat membantu, saya dapat menambahkan (err) bertipe Respon
Mohammed Suez
77

Layanan baru diperbarui untuk menggunakan HttpClientModule dan 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'))
            );
    }

Layanan lama, yang menggunakan HttpModule yang sudah usang:

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

Saya menggunakan .do()( sekarang.tap() ) untuk debugging.

Ketika ada kesalahan server, bodydari Responseobjek saya dapatkan dari server saya menggunakan (lite-server) hanya berisi teks, maka alasan saya menggunakan err.text()di atas bukan err.json().error. Anda mungkin perlu menyesuaikan jalur itu untuk server Anda.

Jika res.json()memunculkan kesalahan karena tidak bisa mengurai data JSON, _serverErrortidak akan mendapatkan Responseobjek, maka alasan untuk instanceofmemeriksa.

Dalam hal ini plunker, ubah urlke ./data/data.junkuntuk menghasilkan kesalahan.


Pengguna dari salah satu layanan harus memiliki kode yang dapat menangani kesalahan:

@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
sumber
4

Ada beberapa cara untuk melakukan ini. Keduanya sangat sederhana. Masing-masing contoh berfungsi dengan baik. Anda dapat menyalinnya ke proyek Anda dan mengujinya.

Metode pertama lebih disukai, yang kedua agak ketinggalan jaman, tetapi sejauh ini berhasil juga.

1) Solusi 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) Solusi 2. Ini cara lama tetapi masih berfungsi.

// 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
sumber
-1

Fungsi RxJS perlu diimpor secara khusus. Cara mudah untuk melakukan ini adalah dengan mengimpor semua fitur-fiturnyaimport * as Rx from "rxjs/Rx"

Kemudian pastikan untuk mengakses Observablekelas sebagai Rx.Observable.

MatthewScarpino
sumber
15
Rxjs adalah file yang sangat besar, jika Anda mengimpor semua fitur itu akan menambah waktu pemuatan Anda
Soumya Gangamwar
Anda seharusnya tidak hanya mengimpor semuanya dari Rxjs jika Anda hanya membutuhkan satu atau dua operator.
marcel-k
-4

dalam versi terbaru penggunaan angular4

import { Observable } from 'rxjs/Rx'

itu akan mengimpor semua hal yang diperlukan.

Munish Sharma
sumber
20
Jangan lakukan ini, itu akan mengimpor semua Rxjs.
marcel-k
Dan juga akan menghasilkan ukuran bundel yang meningkat!
Tushar Walzade