Как правильно поймать исключение из http.request ()?

133

Часть моего кода:

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() выдает исключение в консоли браузера:

ОРИГИНАЛЬНОЕ ИСКЛЮЧЕНИЕ: TypeError: this.http.request (...). Map (...). Catch не является функцией

МНВ
источник

Ответы:

214

Возможно, вы можете попробовать добавить это в свой импорт:

import 'rxjs/add/operator/catch';

Вы также можете:

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

По комментариям:

ИСКЛЮЧЕНИЕ: TypeError: Observable_1.Observable.throw не является функцией

Точно так же для этого вы можете использовать:

import 'rxjs/add/observable/throw';
acdcjunior
источник
2
Спасибо за помощь, работает. После этого у меня такая же проблема с throw()функцией. import 'rxjs/Rx';Вместо этого я добавил эту строку . Теперь все операторы работают исправно.
mnv
Вы смоделировали ошибку, чтобы проверить, .catchдействительно ли она работает? Это .subscribe() точно работает.
acdcjunior
1
Да вторая проблема была EXCEPTION: TypeError: Observable_1.Observable.throw is not a function. Это может быть исправлено с помощью ответа @MattScarpino или с помощью этого плункера, как я сказал выше: angular.io/resources/live-examples/server-communication/ts/…
mnv
16
Просто импортируйте также throw: import 'rxjs/add/observable/throw';и не импортируйте все, это слишком велико.
dfsq
Отличное решение, очень полезно, я мог бы добавить (err) типа Response
Mohammed Suez
77

Новая служба обновлена ​​для использования HttpClientModule и 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'))
            );
    }

Старая служба, использующая устаревший 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);
    }
}

Использую .do()( сейчас.tap() ) для отладки.

Когда есть ошибка сервера, то bodyиз Responseобъекта я получаю от сервера я использую (облегченный-сервер) содержат только текст, следовательно, причину я использую err.text()выше, чем err.json().error. Возможно, вам потребуется настроить эту строку для вашего сервера.

Если res.json()возникает ошибка, потому что он не может проанализировать данные JSON, _serverErrorне получит Responseобъект, следовательно, причина для instanceofпроверки.

В этом случае plunkerизмените значение urlна, ./data/data.junkчтобы сгенерировать ошибку.


У пользователей любой из служб должен быть код, который может обрабатывать ошибку:

@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
            );
    }
}
Марк Райкок
источник
4

Есть несколько способов сделать это. Оба очень простые. Каждый из примеров отлично работает. Вы можете скопировать его в свой проект и протестировать.

Первый способ предпочтительнее, второй немного устарел, но пока тоже работает.

1) Решение 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) Решение 2. Это старый способ, но все еще работает.

// 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);
            }
        );
    }
}
Виктор Исайкин
источник
-1

Функции RxJS необходимо специально импортировать. Легкий способ сделать это - импортировать все его функции с помощьюimport * as Rx from "rxjs/Rx"

Затем убедитесь, что вы получили доступ к Observableклассу как Rx.Observable.

MatthewScarpino
источник
15
Rxjs - очень большой файл, если вы импортируете все его функции, это увеличит ваше время загрузки
Соумья Гангамвар,
Вы не должны просто импортировать все из Rxjs, если вам нужен только один или два оператора.
marcel-k
-4

в последней версии angular4 используйте

import { Observable } from 'rxjs/Rx'

он импортирует все необходимые вещи.

Муниш Шарма
источник
20
Не делайте этого, он импортирует все Rxjs.
marcel-k
И это приведет к увеличению размера пакета!
Tushar Walzade