Reputation: 12212
I have a service which calls a REST API. The create
operation POST the parameters, and then GET the created object by using the id
returned in the HTTP header. When the object is returned, a Subject propagates the object to notify other components. You can notice that I don't call the catch
operation. I did it on purpose so the global error handler manages the error It works as expected the first time, but the second time, the global error handler is not called.
In the Chrome console, I get an error after the first call but not after the second call in rxjs/Subscriber.js
:
Subscriber.js:240 Uncaught
Response {_body: "{"message":"The query is not valid"}", status: 500, ok: false, statusText: "Internal Server Error", headers: Headers…}
SafeSubscriber.prototype.__tryOrUnsub = function (fn, value) {
try {
fn.call(this._context, value);
}
catch (err) {
this.unsubscribe();
throw err;
}
};
The service:
@Injectable()
export class SystemService {
private subject = new Subject<any>();
constructor(private http: Http) {
}
create(system: SdqlSystem) {
this.http.post(environment.apiEndpoint + 'systems', system)
.flatMap(res => {
const location = res.headers.get('Location');
return this.http.get(location);
})
.map(res => res.json())
.subscribe(data => this.subject.next(data));
}
get(id: string) {
this.http.get(environment.apiEndpoint + 'systems/' + id)
.map(res => res.json())
.subscribe(data => this.subject.next(data));
}
systems(): Observable<any> {
return this.subject.asObservable();
}
}
The call from a form:
onSubmit({value, valid}: { value: SdqlSystem, valid: boolean }) {
this.systemService.create(value);
}
The global error handler:
@Injectable()
export class GlobalErrorHandler implements ErrorHandler {
constructor(private logger: LoggingService, private notificationsService: NotificationsService) {
}
handleError(error: Response | any): void {
console.log('***** HANDLE ERROR *****');
let errMsg: string;
if (error instanceof Response) {
const body = error.json() || '';
const err = body.error || body.message || JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
} else {
errMsg = error.message ? error.message : error.toString();
}
this.logger.error(errMsg);
this.notificationsService.error('Error', errMsg);
throw error;
}
}
Upvotes: 3
Views: 2452
Reputation: 1682
OP already resolved it, but I guess I'll answer in case someone else stumbles upon this problem:
Rethrowing the error in the GlobalErrorHandler
leads to an uncaught exception where the browser stops execution. That's why it works only once.
The solution is simply to not rethrow in the ErrorHandler.
More detail: In this case there is no onError
-function provided in subscribe()
. That's why rxjs/Subscriber.js
runs into the catch
-part of __tryOrUnsub
.
It tries to execute the onError
-function but fails (because there is none) and therefore unsubscribes from the Observable and throws the error.
The GlobalErrorHandler
picks it up, does logging and notification and then rethrows it.
At this point the error becomes an uncaught exception outside of Angular.
(the default ErrorHandler does not rethrow an error)
Note: this approach works for standard http-calls, because the Response-Observable only emits one value and then completes anyway. If you have an Observable that needs to emit more than one value you will have to deal with the error differently because an Observable that emits an error will not emit any further values, as specified in the Observable-Contract:
OnError
indicates that the Observable has terminated with a specified error condition and that it will be emitting no further items
Upvotes: 12