camcam
camcam

Reputation: 2625

In Angular2, how to intercept error response from observable and pass it to the error channel

I have a service which interacts with an api server. It has myHttpPost(data) method. This method should:

  1. call Angular's http.post to the server
  2. if response has status 200, provide the response's json content to subscriber
  3. if response has different status, process the response and provide processed error to the subscriber

The current version of my method is:

postData(path: string, data: Object): Rx.Observable<any> {
    let json = JSON.stringify(data);
    return this.http.post(path, json)
        .map(res => res.json())
}

This is not doing point 3 from the above list, because map is only called upon success and not on error. I could change the sequence by adding:

.catch(error => {
    let myError = prepareErrorContent(error);
    return Rx.Observable.throw(myError);
});

Then the subscriber (another service) receives myError (that's ok) but the rxjs documentation says the following about catch() method: "Continues an observable sequence that is terminated by an exception with the next observable sequence." But I want the sequence to terminate normally and not to continue. If I use the catch() like above, then the next call to postData() from the subscriber does not work (it immediately returns the same error instead of making the http call). I think it's because the sequence is not terminated.

How to do it?

[Edit] This is my subscriber method which uses this service:

 this.jsonHttp.postData(path, data)
    .subscribe(
        struct => onNext(struct),
        error => {
            let message = this.jsonHttp.extractError(error);
            onError(message)
        },
        () => onComplete ? onComplete() : null
    );

Upvotes: 11

Views: 21200

Answers (1)

Thierry Templier
Thierry Templier

Reputation: 202138

In fact, for each HTTP request a processing chain is created and executed. When the response is received (both successful and with error), the chain is completed.

If you execute twice the request, there is no link between them. You can have some links when both requests are part of the same processing chain. For example when using observable operators like flatMap or switchMap. Here is a sample:

return this.http.get(...)
        .map(res => res.json())
        .flatMap(jsonData => {
          // Can leverage the result of the previous request 
          return this.http.get(...).map(res => res.json());
        });

When an error occurs, the processing chain is broken and the catch operator is called. You can return an observable in the associated callback to continue the processing chain. It can be a classical observable or an error one with Observable.throw. In the context of an HTTP request in Angular2, a response with a status code different than 2xx is considered as an error.

When returning Observable.throw, the second callback specified when subscribed is called. If it's another observable, the first callback is called. For example:

return this.http.get(...)
        .map(res => res.json())
        .catch(res => {
          // The error callback (second parameter) is called
          return Observable.throw(res.json());
          // The success callback (first parameter) is called
          // return Observable.of(res.json());
        });

I implemented a plunkr that execute a request (clicking on a button) without authentication header, so a 401 status code is received. When you click again, the header is added, so a 200 status code is received. Just to show you that there is no relation between the two requests.

See this plunkr: https://plnkr.co/edit/5WwWpwjvQmJ4AlncKVio?p=preview.

Upvotes: 18

Related Questions