I am implementing login for my nifty new Angular app, and sending login/signup requests to my backend with an @angualar/http post request, doing what I think is a standard post-map-catch paradigm.
My first test was to see how nicely it caught the error when the backend API was not available, and I was surprised and disappointed to find that the '.catch' mechanism didn't do a very good job, and did not catch the underlying error -- instead catching an ugly error Response object.
Here's my code for my LoginService that's being invoked from the component that handles login/signup
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Headers, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import { User } from '../models/user';
@Injectable()
export class LoginService {
private loginUrl = 'http://localhost:300/api/login';
private signupUrl = 'http://localhost:300/api/signup';
//_____________________________________________________________
// Public interface
constructor(private http: Http) {}
LoginUser(user: User): Observable<LoginResponse> {
console.log('LoginService: login request for user %s', user.email);
return this.http.post(this.loginUrl, user)
.map(this.parseData)
.catch(this.handleError);
}
SignupUser(user: User): Observable<SignupResponse> {
console.log('LoginService: signup request for user %s', user.email);
return this.http.post(this.signupUrl, user)
.map(this.parseData)
.catch(this.handleError);
}
//__________________________________________________________________
// Private interface
private parseData(res: Response) {
console.log('LoginService: received response %s', res.json());
return res.json() || {};
}
private handleError(error: Response | any) {
let errorMessage: string;
errorMessage = error.message ? error.message : error.toString();
console.error('LoginService: error: %s', errorMessage);
return Observable.throw(errorMessage);
}
}
export class LoginResponse {
// TODO: implement
foo: number;
}
export class SignupResponse {
// TODO: implement
foo: number;
}
I am purposely testing using a low-numbered port (300) where nothing is listening (and likely access would be denied anyway).
What I see in the Chrome browser console is a very nice error that gets raised:
Followed by the ugly error that ends up getting caught by the .catch() and then logged:
This stackoverflow post helpfully lets me know "You can probably check the status of the ResponseError. When an API is offline that is 0"...which is true, so that's nice.
Why doesn't the nice (at least 'nicer') error get caught and packaged up for the catch? Or perhaps it does get caught and the underlying CONNECTION_RESET part is simply thrown away, along with the URL (I found the for URL: null pretty amusing), and a more nebulous Response object is constructed in order to make it harder for me to concoct a nice error message for my users?