How to catch exception correctly from http.request()?

HttpTypescriptAngularObservable

Http Problem Overview


Part of my 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() produces exception in console of browser:

> ORIGINAL EXCEPTION: TypeError: this.http.request(...).map(...).catch is not a function

Http Solutions


Solution 1 - Http

Perhaps you can try adding this in your imports:

import 'rxjs/add/operator/catch';

You can also do:

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

Per comments: > # EXCEPTION: TypeError: Observable_1.Observable.throw is not a function

Similarly, for that, you can use:

import 'rxjs/add/observable/throw';

Solution 2 - Http

New service updated to use the HttpClientModule and 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'))
            );
    }

Old service, which uses the deprecated 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);
    }
}

I use .do() (now .tap()) for debugging.

When there is a server error, the body of the Response object I get from the server I'm using (lite-server) contains just text, hence the reason I use err.text() above rather than err.json().error. You may need to adjust that line for your server.

If res.json() raises an error because it could not parse the JSON data, _serverError will not get a Response object, hence the reason for the instanceof check.

In this plunker, change url to ./data/data.junk to generate an error.


Users of either service should have code that can handle the error:

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

Solution 3 - Http

There are several ways to do this. Both are very simple. Each of the examples works great. You can copy it into your project and test it.

The first method is preferable, the second is a bit outdated, but so far it works too.

  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. It is old way but still works.

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

    }

Solution 4 - Http

The RxJS functions need to be specifically imported. An easy way to do this is to import all of its features with import * as Rx from "rxjs/Rx"

Then make sure to access the Observable class as Rx.Observable.

Solution 5 - Http

in the latest version of angular4 use

import { Observable } from 'rxjs/Rx'

it will import all the required things.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionNickView Question on Stackoverflow
Solution 1 - HttpacdcjuniorView Answer on Stackoverflow
Solution 2 - HttpMark RajcokView Answer on Stackoverflow
Solution 3 - HttpVictor IsaikinView Answer on Stackoverflow
Solution 4 - HttpMatthewScarpinoView Answer on Stackoverflow
Solution 5 - HttpMunish SharmaView Answer on Stackoverflow