I have an Angular/JHipster application that makes HTTP calls. I want to do error handling in my Observable
subscriptions.
However, when the first time the error handler is invoked, the err
object is not a response, it's a raw {}
Object
. All subsequent error handler invocations have a valid Response
.
Why is this happening, and how do I fix it?
MyComponent.ts:
handleClickPost() {
this.myService.doPost(this.body)
.subscribe(
(res: Response) => {
this.responseOk = true;
},
(err: Response) => {
// This is a HACK!
// For some reason, the first time this error handler is invoked (after page load),
// the response body is empty here and serializes to "{}".
if (JSON.stringify(err) === '{}') {
// first request always reaches here... WHY?
this.handleClickPost(); // NB: workaround
// err.json(); // results in null "method not found" error
} else {
// second request always reaches here... WHY?
// (I want all requests to go here with a valid 'Response' object)
this.showRequestFailed = true;
this.errorResponse = err.json();
}
}
);
}
MyService.ts:
doPost(body): Observable<Response> {
return this.http.post(this.resourceUrl, body);
}
My shoddy workaround is to just call the method again after the first empty-body failure in order to get a typed Response
.
NB: This behavior happens with both Angular 2 and Angular 4.
First you have to check few things to make sure your setup is correct.
Check in server side whether you are sending the proper JSON encoded response for the first time request i.e the response is correctly serialized or not.
check if the payload for the POST request this.body
is already converted to json string or not if you are posting json data to server . You should use
JSON.stringify(this.body)
before sending the request if you are sending json data.
you should use map()/catch() in you post()
method to actually map the response and catch the actual error.
So your code should look like below
MyService.ts:
doPost(body:any): Observable<Response> {
let payload = JSON.stringify(body); // Stringify payload if you are using JSON data
let headers = new Headers({ 'Content-Type': 'application/json' }); // ... Set content type to JSON for json request
let options = new RequestOptions({ headers: headers }); // Create a request option
return this.http.post(this.resourceUrl, payload,options)
.map((res:Response) => res.json()) // ...and calling .json() on the response to return data
.catch((error:any) => Observable.throw(error.json().error || 'Server error')); //...errors if any;
}
MyComponent.ts:
handleClickPost() {
this.myService.doPost(this.body)
.subscribe(
(res: any) => {
this.responseOk = true;
},
(err: any) => {
// Log errors if any
console.log(err);
}
}
);
}
or you can create a separate error handler for the catch()
method like below
private handleError(error: any) {
let errMsg = (error.message) ? error.message : error.status ? `${error.status} - ${error.statusText}` : 'Server error';
console.log(errMsg); // log to console instead
return Observable.throw(errMsg);
}
and use it this way .catch(this.handleError)
in your service class.
Seems like an issue of preflight Http OPTIONS request. Did you verify the response type.
If it is this issue you can avoid the preflight request by modifying the content-header as below.
$http.defaults.headers.post["Content-Type"] = "text/plain"
Read more here: Why am I getting an OPTIONS request instead of a GET request?