Angular HTTP Error Empty Untyped Response

2020-03-01 08:14发布

问题:

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.

回答1:

First you have to check few things to make sure your setup is correct.

  1. 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.

  2. 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.

  3. 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.



回答2:

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?